├── .appveyor.yml
├── .dockerignore
├── .gitattributes
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── Telegram.Bot.Framework.Net45.sln
├── Telegram.Bot.Framework.NetCore.sln
├── Telegram.Bot.Framework.sln
├── docs
├── icon.png
└── wiki
│ ├── README.md
│ ├── deployment
│ ├── docker-letsencrypt.md
│ ├── ubuntu-nginx-selfsigned.md
│ └── ubuntu-nginx.md
│ ├── games
│ └── games-in-telegram.md
│ └── quick-start
│ ├── crazy-circle-game.md
│ └── echo-bot.md
├── sample
├── Quickstart.AspNet45
│ ├── App_Start
│ │ └── WebApiConfig.cs
│ ├── Controllers
│ │ └── WebhookController.cs
│ ├── Global.asax
│ ├── Global.asax.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Quickstart.AspNet45.csproj
│ ├── Web.Debug.config
│ ├── Web.Release.config
│ ├── Web.config
│ └── packages.config
├── Quickstart.AspNetCore
│ ├── EchoBot.cs
│ ├── Extensions
│ │ └── AppStartupExtensions.cs
│ ├── Handlers
│ │ ├── CallbackQueryHandler.cs
│ │ ├── Commands
│ │ │ ├── PingCommand.cs
│ │ │ └── StartCommand.cs
│ │ ├── ExceptionHandler.cs
│ │ ├── StickerHandler.cs
│ │ ├── TextEchoer.cs
│ │ ├── UpdateMembersList.cs
│ │ ├── WeatherReporter.cs
│ │ └── WebhookLogger.cs
│ ├── Options
│ │ └── CustomBotOptions.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Quickstart.AspNetCore.csproj
│ ├── Services
│ │ ├── BotServiceProvider.cs
│ │ ├── CurrentWeather.cs
│ │ ├── IWeatherService.cs
│ │ └── WeatherService.cs
│ ├── Startup.cs
│ ├── When.cs
│ └── appsettings.json
├── Quickstart.Net45
│ ├── EchoBot.cs
│ ├── Handlers
│ │ ├── CallbackQueryHandler.cs
│ │ ├── Commands
│ │ │ ├── PingCommand.cs
│ │ │ └── StartCommand.cs
│ │ ├── ExceptionHandler.cs
│ │ ├── StickerHandler.cs
│ │ ├── TextEchoer.cs
│ │ ├── UpdateMembersList.cs
│ │ ├── WeatherReporter.cs
│ │ └── WebhookLogger.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Quickstart.Net45.csproj
│ ├── Services
│ │ ├── SimpleInjector
│ │ │ └── BotServiceProvider.cs
│ │ └── WeatherService.cs
│ ├── When.cs
│ └── packages.config
├── SampleBots
│ ├── BotUpdateGetterTask.cs
│ ├── Bots
│ │ ├── EchoBot
│ │ │ ├── EchoerBot.cs
│ │ │ └── TextMessageEchoer.cs
│ │ └── GreeterBot
│ │ │ ├── GreeterBot.cs
│ │ │ ├── HiCommand.cs
│ │ │ ├── PhotoForwarder.cs
│ │ │ └── StartCommand.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── SampleBots.csproj
│ ├── Startup.cs
│ └── appsettings.json
├── SampleEchoBot
│ ├── Data
│ │ └── IRepository.cs
│ ├── EchoBot.cs
│ ├── EchoCommand.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── SampleEchoBot.csproj
│ ├── Startup.cs
│ ├── TextMessageHandler.cs
│ └── appsettings.json
└── SampleGames
│ ├── BotUpdateGetterTask.cs
│ ├── Bots
│ └── CrazyCircle
│ │ ├── CrazyCircleBot.cs
│ │ ├── CrazyCircleGameHandler.cs
│ │ └── StartCommand.cs
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── SampleGames.csproj
│ ├── Startup.cs
│ ├── appsettings.json
│ └── wwwroot
│ ├── CrazyCircleBot
│ └── Games
│ │ └── CrazyCircle
│ │ ├── assets
│ │ ├── scripts
│ │ │ ├── TgBF.js
│ │ │ ├── libs
│ │ │ │ ├── easeljs-0.8.0.min.js
│ │ │ │ └── tweenjs-0.6.0.min.js
│ │ │ └── script.js
│ │ └── styles
│ │ │ └── game.css
│ │ └── index.html
│ └── favicon.ico
├── scripts
├── build
│ └── index.js
├── deploy
│ ├── deploy_docker_registry.js
│ ├── deploy_heroku.js
│ ├── deploy_settings.js
│ └── index.js
├── logging.js
├── package-lock.json
├── package.json
├── publish-bots.sh
└── test
│ ├── index.js
│ └── unit_tests.js
├── src
└── Telegram.Bot.Framework
│ ├── ASP.NET Core
│ ├── TelegramBotMiddleware.cs
│ └── TelegramBotMiddlewareExtensions.cs
│ ├── Abstractions
│ ├── IBot.cs
│ ├── IBotBuilder.cs
│ ├── IBotOptions.cs
│ ├── IBotServiceProvider.cs
│ ├── IUpdateContext.cs
│ ├── IUpdateHandler.cs
│ ├── IUpdatePollingManager.cs
│ └── UpdateDelegate.cs
│ ├── BotBase.cs
│ ├── BotOptions.cs
│ ├── CommandBase.cs
│ ├── Extensions
│ └── BotExtensions.cs
│ ├── Telegram.Bot.Framework.csproj
│ ├── Update Pipeline
│ ├── BotBuilder.cs
│ ├── BotBuilderExtensions.cs
│ ├── MapWhenMiddleware.cs
│ └── UseWhenMiddleware.cs
│ ├── UpdateContext.cs
│ └── UpdatePollingManager.cs
└── test
├── UnitTests.Net45
├── Command Handling.cs
├── Properties
│ └── AssemblyInfo.cs
├── UnitTests.Net45.csproj
├── app.config
└── packages.config
└── UnitTests.NetCore
├── Commands
├── Command Handling.cs
├── Command Parsing.cs
└── MockCommand.cs
├── UnitTests.NetCore.csproj
└── test-update.json
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2017
2 |
3 | nuget:
4 | disable_publish_on_pr: true
5 |
6 | before_build:
7 | - nuget restore Telegram.Bot.Framework.sln
8 |
9 | configuration:
10 | - Debug
11 | - Release
12 |
13 | build:
14 | project: Telegram.Bot.Framework.sln
15 | publish_nuget: true
16 |
17 | # test:
18 | # assemblies:
19 | # only:
20 | # - '**\*Tests*.dll'
21 |
22 | deploy:
23 | - provider: NuGet
24 | artifact: /.*Telegram.Bot.Framework.*\.nupkg/
25 | api_key:
26 | secure: StZxOHLR6O7NZ2xY4nVNWoiFNi9Kz8vQFpF1SyWzsZJbvcralJEKpnqOAIHX3qtH
27 | on:
28 | branch: /r\/.+/
29 | configuration: Release
30 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .dockerignore
2 | .env
3 | .git
4 | .gitignore
5 | .vs
6 | .vscode
7 | */bin
8 | */obj
9 | **/.toolstarget
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | *.pubxml*
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Microsoft Azure ApplicationInsights config file
170 | ApplicationInsights.config
171 |
172 | # Windows Store app package directory
173 | AppPackages/
174 | BundleArtifacts/
175 |
176 | # Visual Studio cache files
177 | # files ending in .cache can be ignored
178 | *.[Cc]ache
179 | # but keep track of directories ending in .cache
180 | !*.[Cc]ache/
181 |
182 | # Others
183 | ClientBin/
184 | [Ss]tyle[Cc]op.*
185 | ~$*
186 | *~
187 | *.dbmdl
188 | *.dbproj.schemaview
189 | *.pfx
190 | *.publishsettings
191 | node_modules/
192 | orleans.codegen.cs
193 |
194 | # RIA/Silverlight projects
195 | Generated_Code/
196 |
197 | # Backup & report files from converting an old project file
198 | # to a newer Visual Studio version. Backup files are not needed,
199 | # because we have git ;-)
200 | _UpgradeReport_Files/
201 | Backup*/
202 | UpgradeLog*.XML
203 | UpgradeLog*.htm
204 |
205 | # SQL Server files
206 | *.mdf
207 | *.ldf
208 |
209 | # Business Intelligence projects
210 | *.rdl.data
211 | *.bim.layout
212 | *.bim_*.settings
213 |
214 | # Microsoft Fakes
215 | FakesAssemblies/
216 |
217 | # GhostDoc plugin setting file
218 | *.GhostDoc.xml
219 |
220 | # Node.js Tools for Visual Studio
221 | .ntvs_analysis.dat
222 |
223 | # Visual Studio 6 build log
224 | *.plg
225 |
226 | # Visual Studio 6 workspace options file
227 | *.opt
228 |
229 | # Visual Studio LightSwitch build output
230 | **/*.HTMLClient/GeneratedArtifacts
231 | **/*.DesktopClient/GeneratedArtifacts
232 | **/*.DesktopClient/ModelManifest.xml
233 | **/*.Server/GeneratedArtifacts
234 | **/*.Server/ModelManifest.xml
235 | _Pvt_Extensions
236 |
237 | # LightSwitch generated files
238 | GeneratedArtifacts/
239 | ModelManifest.xml
240 |
241 | # Paket dependency manager
242 | .paket/paket.exe
243 |
244 | # FAKE - F# Make
245 | .fake/
246 |
247 | # Settings for specific environments
248 | appsettings.*.json
249 |
250 | # Rider IDE
251 | .idea
252 |
253 | # Deployments files
254 | /dist/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | sudo: required
3 | language: node_js
4 | node_js:
5 | - node
6 | notifications:
7 | email: false
8 | services:
9 | - docker
10 |
11 | install:
12 | - cd scripts && npm install && cd ..
13 |
14 | script:
15 | - node scripts/build
16 | # - node scripts/test
17 |
18 | deploy:
19 | - provider: script
20 | skip_cleanup: true
21 | script: node scripts/deploy Production
22 | on:
23 | branch: master
24 |
25 |
26 | # Disable "Build pushed pull requests"
27 |
28 |
29 | # env:
30 | # DEPLOY_SETTINGS_JSON: A mapping of environment to the deployment types and their options.
31 | # {
32 | # "Production": [
33 | # { "type": "heroku",
34 | # "options": {
35 | # "app": "foo-staging", "source": "foo:latest", "dyno": "web", "user": "foo@example.org", "token": "TOKEN"
36 | # }
37 | # }
38 | # ]
39 | # }
40 | #####
41 | # export DEPLOY_SETTINGS_JSON='{"Production":[{"type":"heroku","options": {"app": "sample-tgbot","source":"quickstart:latest","dyno":"web","user":"foo@example.org","token":"TOKEN"}}]}'
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
2 | WORKDIR /app
3 |
4 | FROM microsoft/dotnet:2.1-sdk AS build
5 | WORKDIR /src
6 | COPY ["sample/Quickstart.AspNetCore/Quickstart.AspNetCore.csproj", "sample/Quickstart.AspNetCore/"]
7 | RUN dotnet restore "sample/Quickstart.AspNetCore/Quickstart.AspNetCore.csproj"
8 | COPY . .
9 | WORKDIR "/src/sample/Quickstart.AspNetCore"
10 | RUN dotnet build "Quickstart.AspNetCore.csproj" -c Release -o /app
11 |
12 | FROM build AS publish
13 | RUN dotnet publish "Quickstart.AspNetCore.csproj" -c Release -o /app
14 |
15 | FROM base AS final
16 | WORKDIR /app
17 | COPY --from=publish /app .
18 | CMD ASPNETCORE_URLS=http://+:${PORT:-80} dotnet Quickstart.AspNetCore.dll
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Poulad Ashraf pour
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Telegram Bot Framework for .NET Core
2 |
3 | [](https://www.nuget.org/packages/Telegram.Bot.Framework)
4 | [](https://travis-ci.org/pouladpld/Telegram.Bot.Framework)
5 | [](https://raw.githubusercontent.com/pouladpld/Telegram.Bot.Framework/master/LICENSE)
6 |
7 |
8 |
9 | Simple framework for building Telegram bots 🤖. Ideal for running multiple chat bots inside a single ASP.NET Core app.
10 |
11 | See some **sample bots** in action:
12 |
13 | - Echo bot: [`@Sample_Echoer_Bot`](https://t.me/sample_echoer_bot)
14 | - Games bot: [`@CrazyCircleBot`](https://t.me/CrazyCircleBot)
15 |
16 | ## Getting Started
17 |
18 | This project targets .NET Standard 1.6 so make sure you have Visual Studio 2017 or [.NET Core](https://www.microsoft.com/net/download/core#/current) (v1.1 or above) installed.
19 |
20 | Creating a bot with good architecture becomes very simple using this framework. Have a look at the [**Quick Start** wiki](./docs/wiki/quick-start/echo-bot.md) to make your fist _Echo Bot_.
21 |
22 | There is much more you can do with your bot. See what's available at [**wikis**](./docs/wiki/README.md).
23 |
24 | ## Framework Features
25 |
26 | - Allows you to have multiple bots running inside one app
27 | - Able to share code(update handlers) between multiple bots
28 | - Easy to use with webhooks(specially with Docker deployments)
29 | - Optimized for making Telegram Games
30 | - Simplifies many repititive tasks in developing bots
31 |
32 | ## Samples
33 |
34 | Don't wanna read wikis? Read C# code of sample projects in [samples directory](./sample/).
35 |
--------------------------------------------------------------------------------
/Telegram.Bot.Framework.Net45.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26430.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{807C83E6-6E64-49CF-9D3B-BEF1DB6A5534}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telegram.Bot.Framework", "src\Telegram.Bot.Framework\Telegram.Bot.Framework.csproj", "{EC991578-D67E-4208-B540-7FBB603B7D8F}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A77F7142-8E1D-4843-B862-12D7D4A8CD67}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.Net45", "test\UnitTests.Net45\UnitTests.Net45.csproj", "{4E485E65-FBDA-400B-9393-6E70C9E19C3D}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quickstart.Net45", "sample\Quickstart.Net45\Quickstart.Net45.csproj", "{C3672842-82EE-49F3-90A1-0F07A7857EBE}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quickstart.AspNet45", "sample\Quickstart.AspNet45\Quickstart.AspNet45.csproj", "{19B20342-5F00-4B54-B33A-7FD676590208}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {19B20342-5F00-4B54-B33A-7FD676590208}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {19B20342-5F00-4B54-B33A-7FD676590208}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {19B20342-5F00-4B54-B33A-7FD676590208}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {19B20342-5F00-4B54-B33A-7FD676590208}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(NestedProjects) = preSolution
47 | {EC991578-D67E-4208-B540-7FBB603B7D8F} = {807C83E6-6E64-49CF-9D3B-BEF1DB6A5534}
48 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D} = {A77F7142-8E1D-4843-B862-12D7D4A8CD67}
49 | {C3672842-82EE-49F3-90A1-0F07A7857EBE} = {A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}
50 | {19B20342-5F00-4B54-B33A-7FD676590208} = {A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {12F28A44-91F3-4309-8F0B-4C6ED9346D38}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/Telegram.Bot.Framework.NetCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26430.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{807C83E6-6E64-49CF-9D3B-BEF1DB6A5534}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telegram.Bot.Framework", "src\Telegram.Bot.Framework\Telegram.Bot.Framework.csproj", "{EC991578-D67E-4208-B540-7FBB603B7D8F}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A77F7142-8E1D-4843-B862-12D7D4A8CD67}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.NetCore", "test\UnitTests.NetCore\UnitTests.NetCore.csproj", "{D7706BF5-300E-4705-86FA-ECD073EF3F7E}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quickstart.AspNetCore", "sample\Quickstart.AspNetCore\Quickstart.AspNetCore.csproj", "{1CF205C0-A8C2-4F47-A5A6-ED53729B567D}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(NestedProjects) = preSolution
41 | {EC991578-D67E-4208-B540-7FBB603B7D8F} = {807C83E6-6E64-49CF-9D3B-BEF1DB6A5534}
42 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E} = {A77F7142-8E1D-4843-B862-12D7D4A8CD67}
43 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D} = {A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}
44 | EndGlobalSection
45 | GlobalSection(ExtensibilityGlobals) = postSolution
46 | SolutionGuid = {12F28A44-91F3-4309-8F0B-4C6ED9346D38}
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/Telegram.Bot.Framework.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26430.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{807C83E6-6E64-49CF-9D3B-BEF1DB6A5534}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telegram.Bot.Framework", "src\Telegram.Bot.Framework\Telegram.Bot.Framework.csproj", "{EC991578-D67E-4208-B540-7FBB603B7D8F}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A77F7142-8E1D-4843-B862-12D7D4A8CD67}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.Net45", "test\UnitTests.Net45\UnitTests.Net45.csproj", "{4E485E65-FBDA-400B-9393-6E70C9E19C3D}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.NetCore", "test\UnitTests.NetCore\UnitTests.NetCore.csproj", "{D7706BF5-300E-4705-86FA-ECD073EF3F7E}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quickstart.Net45", "sample\Quickstart.Net45\Quickstart.Net45.csproj", "{C3672842-82EE-49F3-90A1-0F07A7857EBE}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quickstart.AspNet45", "sample\Quickstart.AspNet45\Quickstart.AspNet45.csproj", "{19B20342-5F00-4B54-B33A-7FD676590208}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quickstart.AspNetCore", "sample\Quickstart.AspNetCore\Quickstart.AspNetCore.csproj", "{1CF205C0-A8C2-4F47-A5A6-ED53729B567D}"
23 | EndProject
24 | Global
25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
26 | Debug|Any CPU = Debug|Any CPU
27 | Release|Any CPU = Release|Any CPU
28 | EndGlobalSection
29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
30 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {EC991578-D67E-4208-B540-7FBB603B7D8F}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {19B20342-5F00-4B54-B33A-7FD676590208}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {19B20342-5F00-4B54-B33A-7FD676590208}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {19B20342-5F00-4B54-B33A-7FD676590208}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 | {19B20342-5F00-4B54-B33A-7FD676590208}.Release|Any CPU.Build.0 = Release|Any CPU
50 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D}.Release|Any CPU.Build.0 = Release|Any CPU
54 | EndGlobalSection
55 | GlobalSection(SolutionProperties) = preSolution
56 | HideSolutionNode = FALSE
57 | EndGlobalSection
58 | GlobalSection(NestedProjects) = preSolution
59 | {EC991578-D67E-4208-B540-7FBB603B7D8F} = {807C83E6-6E64-49CF-9D3B-BEF1DB6A5534}
60 | {4E485E65-FBDA-400B-9393-6E70C9E19C3D} = {A77F7142-8E1D-4843-B862-12D7D4A8CD67}
61 | {D7706BF5-300E-4705-86FA-ECD073EF3F7E} = {A77F7142-8E1D-4843-B862-12D7D4A8CD67}
62 | {C3672842-82EE-49F3-90A1-0F07A7857EBE} = {A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}
63 | {19B20342-5F00-4B54-B33A-7FD676590208} = {A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}
64 | {1CF205C0-A8C2-4F47-A5A6-ED53729B567D} = {A5BE1637-1C1D-49F4-B98D-C1CD734F1CF1}
65 | EndGlobalSection
66 | GlobalSection(ExtensibilityGlobals) = postSolution
67 | SolutionGuid = {12F28A44-91F3-4309-8F0B-4C6ED9346D38}
68 | EndGlobalSection
69 | EndGlobal
70 |
--------------------------------------------------------------------------------
/docs/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TelegramBots/Telegram.Bot.Framework/6ac27f80d1ae29f86ccf790d7d2a5b21759182ec/docs/icon.png
--------------------------------------------------------------------------------
/docs/wiki/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to `Telegram.Bot.Framework` wikis
2 |
3 | ## Quick Start
4 |
5 | If you are looking for a few clues to begin with, look at the guides here.
6 |
7 | > Make sure you have registered a bot and have its API token. Talk to **[BotFather](http://t.me/botfather)** on Telegram to get one.
8 |
9 | ### 🙄 I'm new to this. Show me how to run a bot 🤖
10 |
11 | - [How about a quick **Echo Bot**?](./quick-start/echo-bot.md)
12 |
13 | ### Telegram games 🎮 are fun. I'm ready to have fun 😎
14 |
15 | - [Crazy Circle 🔴 Game](./quick-start/crazy-circle-game.md)
16 |
17 | ## Games
18 |
19 | Developing Telegram games is not as easy as an Echo Bot. Check the wikis in this section to
20 | get some help.
21 |
22 | - [Games in Telegram](./games/games-in-telegram.md)
23 |
24 | ## Deployment
25 |
26 | This section helps you to deploy your bot on a server and set its webhook.
27 |
28 | - [Deploy to Docker with Let's Encrypt Certificate](./deployment/docker-letsencrypt.md)
29 | - [Deploy to Ubuntu with Nginx and Self-Signed Certificate](./deployment/ubuntu-nginx-selfsigned.md)
30 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/docker-letsencrypt.md:
--------------------------------------------------------------------------------
1 | # Deploy to Docker with Let's Encrypt Certificate
2 |
3 | ## ToDo
4 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/ubuntu-nginx-selfsigned.md:
--------------------------------------------------------------------------------
1 | # Deploy to Ubuntu with Nginx and Self-Signed Certificate
2 |
3 | This tutorial shows you how to deploy your bot to an **Ubuntu 16.04** server, use **Nginx** as its reverse proxy,
4 | and setup **webhook**.
5 |
6 | A self-signed certificate is generated and used in this process. ASP.NET Core application
7 | is deployed to Ubuntu server in a Framework Dependant Deployment manner.
8 |
9 | [SampleEchoBot project](../../../sample/SampleEchoBot/) is used in this tutorial for simplicity.
10 |
11 | First of all, make sure you have installed [.NET Core SDK](https://www.microsoft.com/net/core#linuxubuntu) and
12 | [Nginx](https://help.ubuntu.com/community/Nginx) on the server.
13 |
14 | > `www.example.com` represents our arbitrary domain name here.
15 |
16 | ## TLS Certificate
17 |
18 | Telegram uses your bot's certificate to authorize and encrypt webhook messages to bot. Read more about it on
19 | Telegram documentations [here](https://core.telegram.org/bots/api#setwebhook) and [here](https://core.telegram.org/bots/self-signed)
20 |
21 | If you don't have a trusted TLS certificate, use the command below to generate a self-signed certificate.
22 |
23 | ```bash
24 | openssl req -newkey rsa:2048 -sha256 -nodes -keyout sample-echobot.key -x509 -days 365 -out sample-echobot.pem -subj "/C=CA/ST=Ontario/L=Toronto/O=Telegram Bot Framework Organization/CN=example.com"
25 | ```
26 |
27 | > Note that the CN, `example.com` here, should exactly match the domain name in webhook URL you set
28 | in bot's settings.
29 |
30 | > If you don't have a DNS name for your server, you might be able to obtain one for free from [Freenom](https://www.freenom.com).
31 |
32 | Copy bot certificate files to Nginx configuration directory.
33 |
34 | ```bash
35 | sudo mkdir /etc/nginx/certificates
36 | sudo cp sample-echobot.{key,pem} /etc/nginx/certificates
37 | sudo chown www-data:www-data /etc/nginx/certificates/sample-echobot.{key,pem}
38 | sudo chmod 400 /etc/nginx/certificates/sample-echobot.key
39 | ```
40 |
41 | ## Publish
42 |
43 | Get the source code and with .NET Core SDK installed, build the app.
44 |
45 | ```bash
46 | git clone "https://github.com/pouladpld/Telegram.Bot.Framework.git"
47 |
48 | cd Telegram.Bot.Framework
49 |
50 | dotnet restore
51 | dotnet build
52 |
53 | cd src/Telegram.Bot.Sample/
54 | dotnet publish -c Release -o bin/publish
55 | ```
56 |
57 | Create app's directory, give it necessary permissions and copy the app to it.
58 |
59 | ```bash
60 | sudo mkdir -p /var/www/aspnet/sample-echobot/
61 | sudo chown -R :www-data /var/www/aspnet/sample-echobot
62 | sudo chmod -R g+s /var/www/aspnet/sample-echobot
63 |
64 | sudo cp -r bin/publish/* /var/www/aspnet/sample-echobot/
65 | ```
66 |
67 | ## App Configurations
68 |
69 | Create file `/var/www/aspnet/sample-echobot/appsettings.Production.json`
70 | and store the configurations there.
71 |
72 | ```json
73 | {
74 | "EchoBot": {
75 | "ApiToken": "{your-api-token}",
76 | "BotUserName": "{your-bot-username}",
77 | "PathToCertificate": "/etc/nginx/certificates/sample-echobot.pem",
78 | "WebhookUrl": "https://example.com/bots/{bot}/webhook/{token}"
79 | }
80 | }
81 | ```
82 |
83 | > Replace the values for _ApiToken_ and _BotUserName_, and domain name in _WebhookUrl_.
84 |
85 | ## App Service
86 |
87 | Add a system service for bot. Create file `/etc/systemd/system/sample-echobot.service` and
88 | write the following configuration to it.
89 |
90 | ```text
91 | [Unit]
92 | Description=Sample Echo Bot
93 |
94 | [Service]
95 | ExecStart=/bin/bash -c "cd /var/www/aspnet/sample-echobot && dotnet ./SampleEchoBot.dll"
96 | Restart=always
97 | RestartSec=10
98 | SyslogIdentifier=sample-echobot
99 | User=www-data
100 | Environment=ASPNETCORE_ENVIRONMENT=Production
101 |
102 | [Install]
103 | WantedBy=multi-user.target
104 | ```
105 |
106 | Reload the system settings so the new service will be available.
107 |
108 | ```bash
109 | sudo systemctl daemon-reload
110 | ```
111 |
112 | ## Nginx
113 |
114 | First of all, make a backup of default site's configuration.
115 |
116 | ```bash
117 | sudo cp /etc/nginx/sites-available/default{,~}
118 | ```
119 |
120 | Open file `/etc/nginx/sites-available/default` and edit Nginx configurations:
121 |
122 | ```nginx
123 | server {
124 | # Change domain name here
125 | server_name example.com localhost;
126 |
127 | listen 80;
128 | listen [::]:80;
129 |
130 | root /var/www/html;
131 | index index.html index.htm index.nginx-debian.html;
132 |
133 | location / {
134 | try_files $uri $uri/ =404;
135 | }
136 |
137 | location ~* ^/bots/.+/webhook/.+$ {
138 | return 301 https://$host$request_uri;
139 | }
140 | }
141 |
142 | server {
143 | # Change domain name here
144 | server_name example.com localhost;
145 |
146 | listen 443 ssl;
147 | listen 8080 ssl;
148 | listen 8443 ssl;
149 |
150 | ssl_certificate /etc/nginx/certificates/sample-echobot.pem;
151 | ssl_certificate_key /etc/nginx/certificates/sample-echobot.key;
152 |
153 | ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
154 | ssl_prefer_server_ciphers on;
155 |
156 | # Change {your-bot-username} here
157 | location ~* ^/bots/{your-bot-username}/webhook/.+$ {
158 | proxy_pass http://0.0.0.0:5000;
159 | proxy_http_version 1.1;
160 | proxy_set_header Upgrade $http_upgrade;
161 | proxy_set_header Connection keep-alive;
162 | proxy_set_header Host $host;
163 | proxy_cache_bypass $http_upgrade;
164 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
165 | }
166 | }
167 | ```
168 |
169 | > Replace values for domain name in _server_name_ and _{your-bot-username}_ in location url.
170 |
171 | Test new Nginx configurations and restart the web server.
172 |
173 | ```bash
174 | sudo nginx -t && sudo systemctl restart nginx
175 | ```
176 |
177 | ## Start
178 |
179 | That's all. Start the app. App sets webhook at startup and you should be able to chat with it.
180 | Try `/echo` command in chat.
181 |
182 | ```bash
183 | sudo systemctl start sample-echobot
184 |
185 | # See app logs
186 | sudo journalctl --identifier=sample-echobot --follow
187 | ```
188 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/ubuntu-nginx.md:
--------------------------------------------------------------------------------
1 | # Deploy a Telegram bot and run it with Webhooks
2 |
3 | In this tutorial, I discuss how to configure and deploy a Telegram bot to an **Ubuntu 17.04 64bit**. Project [Telegram.Bot.Sample](https://github.com/pouladpld/Telegram.Bot.Framework/tree/master/src/Telegram.Bot.Sample) is deployed and it contains two Telegram bots inside.
4 |
5 | ## TLS Certificate
6 |
7 | If you don't have a TLS certificate, use the command below to generate a self-signed certificate.
8 |
9 | ```bash
10 | openssl req -newkey rsa:2048 -sha256 -nodes -keyout samplebot.key -x509 -days 365 -out samplebot.pem -subj "/C=CA/ST=Ontario/L=Toronto/O=.NET Telegram Bot Framework Organization/CN=example.org"
11 | ```
12 |
13 | > Replace the values such as `example.org` in the above command.
14 |
15 | Note that the CN, `example.org` here, should exactly match the webhook URL you send to Telegram. For keeping this tutorial simple, I am using 1 certificate for both of the bots.
16 |
17 | ## .NET Core CLI
18 |
19 | Install [.NET Core](https://www.microsoft.com/net/core#linuxubuntu)
20 |
21 | > I installed version `dotnet-dev-2.0.0-preview1-005977` for this tutorial because I had problems with installing version `dotnet-dev-1.0.4`.
22 |
23 | ## Application Service
24 |
25 | ```bash
26 | sudo vi /etc/systemd/system/samplebot.service
27 | ```
28 |
29 | content:
30 |
31 | ```text
32 | [Unit]
33 | Description=SampleBot - Telegram bot app
34 |
35 | [Service]
36 | ExecStart=/bin/bash -c "cd /var/www/aspnet/samplebot && ./Telegram.Bot.Sample"
37 | Restart=always
38 | RestartSec=10
39 | SyslogIdentifier=samplebot
40 | User=www-data
41 | Environment=ASPNETCORE_ENVIRONMENT=Production
42 |
43 | [Install]
44 | WantedBy=multi-user.target
45 | ```
46 |
47 | ```bash
48 | sudo systemctl daemon-reload
49 | ```
50 |
51 | ## Nginx
52 |
53 | ```bash
54 | sudo apt-get install nginx -y
55 | ```
56 |
57 | Create directories to publish the app to.
58 |
59 | ```bash
60 | sudo mkdir -p /var/www/aspnet/samplebot
61 | sudo chown -R :www-data /var/www/aspnet/samplebot
62 | sudo chmod -R g+s /var/www/aspnet/samplebot
63 | ```
64 |
65 | Copy the bot certificates to Nginx configuration directory.
66 |
67 | ```bash
68 | sudo mkdir /etc/nginx/certificates
69 | sudo cp samplebot.{key,pem} /etc/nginx/certificates
70 | sudo chown www-data:www-data /etc/nginx/certificates/samplebot.{key,pem}
71 | sudo chmod 400 /etc/nginx/certificates/samplebot.key
72 | ```
73 |
74 | ### Site Configuration
75 |
76 | ```bash
77 | # Make a backup of default site's configuration
78 | sudo cp /etc/nginx/sites-available/default{,~}
79 |
80 | sudo vi /etc/nginx/sites-available/default
81 | ```
82 |
83 | Content:
84 |
85 | ```nginx
86 | server {
87 | server_name samplebot.com localhost;
88 | listen 80;
89 | listen [::]:80;
90 |
91 | root /var/www/html;
92 |
93 | index index.html index.htm index.nginx-debian.html;
94 |
95 | location / {
96 | try_files $uri $uri/ =404;
97 | }
98 |
99 | location /sample_greeter_bot {
100 | proxy_pass http://localhost:5000;
101 | }
102 |
103 | location /sample_echoer_bot {
104 | proxy_pass http://localhost:5000;
105 | }
106 | }
107 |
108 | server {
109 | server_name samplebot.com localhost;
110 |
111 | listen 443 ssl;
112 | listen 8080 ssl;
113 | listen 8443 ssl;
114 |
115 | ssl_certificate /etc/nginx/certificates/samplebot.pem;
116 | ssl_certificate_key /etc/nginx/certificates/samplebot.key;
117 |
118 | ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
119 | ssl_prefer_server_ciphers on;
120 |
121 | location /sample_greeter_bot {
122 | proxy_pass http://localhost:5000;
123 | proxy_http_version 1.1;
124 | proxy_set_header Upgrade $http_upgrade;
125 | proxy_set_header Connection keep-alive;
126 | proxy_set_header Host $host;
127 | proxy_cache_bypass $http_upgrade;
128 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
129 | }
130 |
131 | location /sample_echoer_bot {
132 | proxy_pass http://localhost:5000;
133 | proxy_http_version 1.1;
134 | proxy_set_header Upgrade $http_upgrade;
135 | proxy_set_header Connection keep-alive;
136 | proxy_set_header Host $host;
137 | proxy_cache_bypass $http_upgrade;
138 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
139 | }
140 | }
141 | ```
142 |
143 | ```bash
144 | sudo nginx -t
145 | sudo systemctl restart nginx
146 | ```
147 |
148 | ## Publish the App
149 |
150 | ```bash
151 | mkdir source-code && cd source-code
152 | git clone https://github.com/pouladpld/Telegram.Bot.Framework.git .
153 |
154 | # Build the project
155 | dotnet restore -r ubuntu.16.10-x64
156 |
157 | cd src/Telegram.Bot.Sample
158 | # rm -rf bin/publish/*
159 | dotnet publish -c Release -o bin/publish -r ubuntu.16.10-x64
160 |
161 | # sudo rm -rf /var/www/aspnet/samplebot/*
162 | sudo cp -r bin/publish/* /var/www/aspnet/samplebot/
163 | sudo chmod ug+x /var/www/aspnet/samplebot/Telegram.Bot.Sample
164 | ```
165 |
166 | In addition to _appsettings.json_ file, you can store the production secrets in a file like the one below:
167 |
168 | ```json
169 | // appsettings.Production.json
170 | {
171 | "EchoerBot": {
172 | "ApiToken": "",
173 | "PathToCertificate": "/etc/nginx/certificates/samplebot.pem",
174 | "WebhookUrl": "https://example.org/{botname}/{token}/webhook"
175 | },
176 | "GreeterBot": {
177 | "ApiToken": "",
178 | "PathToCertificate": "/etc/nginx/certificates/samplebot.pem",
179 | "WebhookUrl": "https://example.org/{botname}/{token}/webhook"
180 | }
181 | }
182 | ```
183 |
184 | ### Run the app
185 |
186 | ```bash
187 | sudo systemctl start samplebot.service
188 | ```
189 |
--------------------------------------------------------------------------------
/docs/wiki/games/games-in-telegram.md:
--------------------------------------------------------------------------------
1 | # Games in Telegram
2 |
3 | Games are simply HTML5(HTML/CSS/JS) pages loaded into a Telegram client's browser. Users also
4 | have option to open the game page in a browser. Check Telegram's post about [Gaming Platform](https://telegram.org/blog/games)
5 | to learn more.
6 |
7 | For example, [LumberJack](https://telegram.me/gamebot?game=Lumberjack) is a Telegram game. Go ahead and
8 | play it!
9 |
10 | ## Creating a Game
11 |
12 | This all happens in your chat with [BotFather](http://t.me/botfather). Enable [Inline Mode](https://core.telegram.org/bots/#inline-mode) for your bot and use `/newgame` command to create your game. You can read more about it [here](https://core.telegram.org/bots/games#creating-a-game).
13 |
14 | Each game has a _short name_ that is game's unique id whithin a bot.
15 |
16 | ## Redirecting user to Game
17 |
18 | In the chat, when user clicks on _Play LumberJack_, for instance, your bot receives a CallbackQuery update
19 | containing `game_short_name`. In response, bot makes a `answerCallbackQuery` request passing the url to the
20 | game's page. Telegram client opens the browser and the fun starts!
21 |
22 | For example, start [LumberJack](https://telegram.me/gamebot?game=Lumberjack) game on your phone and click on
23 | _Open in..._ while in game to open it in a browser. You will see the link to game is something like:
24 |
25 | `https://tbot.xyz/lumber/#{some-encoded-value}&tgShareScoreUrl=tgb://share_game_score?hash={some-hash-value}`
26 |
27 | ## High Scores
28 |
29 | Games can set high scores for users in the chat that they got opened from. This means when game is finished,
30 | a request is usually made to backend (bot's ASP.NET Core app) and bot makes a `setGameScore` call to Telegram API.
31 |
32 | Similarly, for getting high scores, a request from HTML page(game) could be sent to backend. Then, bot makes a `getGameHighScores` call to Bot API and passes back the high scores to the game.
33 |
34 | ## Deoployment
35 |
36 | Your Game's HTML page can be anywhere on the internet. It's worth mentioning as a web developer, you should consider many things in your deployment such as:
37 |
38 | - Preferring HTTPS over HTTP
39 | - Using a trusted TLS certificate
40 | - Allowing Cross-Origin requests, if necessary
41 | - Guarding against attacks
42 | - Preventing cheaters
43 | - And many more...
44 |
45 | > Sample games in this project are kept simple and do not follow best practices.
46 |
--------------------------------------------------------------------------------
/docs/wiki/quick-start/crazy-circle-game.md:
--------------------------------------------------------------------------------
1 | # Crazy Circle Game
2 |
3 | Crazy Circle 🔴 is a sample game to demo this framework's features.
4 |
5 | You can 🎮 [play it on Telegram](https://telegram.me/CrazyCircleBot?game=crazycircle) 🎮.
6 |
7 | Telegram games are just a HTML5 page loaded in browser inside Telegram client
8 | app or in user's browser. Luckily, you can just go ahead and **use Crazy Circle sample**
9 | to begin with.
10 |
11 | This guide assumes that:
12 |
13 | - You already completed [Echo Bot](./echo-bot.md) quick start
14 | - You are able to deploy the project. See [deployment guides](../README.md#deployment)
15 | - Your bot's user name is `CrazyCircleBot` (Replace it with your own bot's user name)
16 |
17 | ## New Telegarm Game
18 |
19 | Have a chat with **[BotFather](http://t.me/botfather)** to create a new game for your bot and
20 | **set its short_name to `crazycircle`**.
21 |
22 | ## Project Setup
23 |
24 | Create a new ASP.NET Core app and add the following NuGet packages to it:
25 |
26 | - `Telegram.Bot.Framework`
27 | - `Microsoft.AspNetCore.StaticFiles`
28 | - `Microsoft.AspNetCore.DataProtection`
29 |
30 | Copy files from [`sample/SampleGames/wwwroot/bots`](../../../sample/SampleGames/wwwroot/bots/)
31 | into this project's `wwwroot/bots/` directory.
32 |
33 | Rename the bot's direcotry name according to this format: `/wwwroot/bots/{your-bot-user-name}/`.
34 |
35 | ## Code
36 |
37 | ### Bot
38 |
39 | Create your bot class:
40 |
41 | ```c#
42 | public class CrazyCircleBot : BotBase {
43 | public CrazyCircleBot(IOptions> botOptions)
44 | : base(botOptions) { }
45 | public override Task HandleUnknownMessage(Update update) => Task.CompletedTask;
46 | public override Task HandleFaultedUpdate(Update update, Exception e) => Task.CompletedTask;
47 | }
48 | ```
49 |
50 | ### Update Handlers
51 |
52 | Create a `/start` command that replies with the CrazyCircle game:
53 |
54 | ```c#
55 | public class StartCommandArgs : ICommandArgs {
56 | public string RawInput { get; set; }
57 | public string ArgsInput { get; set; }
58 | }
59 |
60 | public class StartCommand : CommandBase {
61 | public StartCommand() : base(name: "start") {}
62 |
63 | public override async Task HandleCommand(Update update, StartCommandArgs args) {
64 | await Bot.Client.SendGameAsync(update.Message.Chat.Id, gameShortName: "crazycircle");
65 | return UpdateHandlingResult.Handled;
66 | }
67 | }
68 | ```
69 |
70 | And add the game handler:
71 |
72 | ```c#
73 | public class CrazyCircleGameHandler : GameHandlerBase {
74 | public CrazyCircleGameHandler(IDataProtectionProvider protectionProvider)
75 | : base(protectionProvider, shortName: "crazycircle") {}
76 | }
77 | ```
78 |
79 | ### Startup
80 |
81 | In `Startup` class, add the following code:
82 |
83 | ```c#
84 | public void ConfigureServices(IServiceCollection services) {
85 | services.AddDataProtection();
86 | services.AddTelegramBot(_configuration.GetSection("CrazyCircleBot"))
87 | .AddUpdateHandler()
88 | .AddUpdateHandler()
89 | .Configure();
90 | }
91 |
92 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
93 | app.UseStaticFiles();
94 | app.UseTelegramGame();
95 | app.UseTelegramBotWebhook();
96 | }
97 | ```
98 |
99 | ## Configurations
100 |
101 | Add the following configurations to your `appsettings.json` or use any other similar method.
102 |
103 | ```json
104 | {
105 | "CrazyCircleBot": {
106 | "ApiToken": "",
107 | "BotUserName": "CrazyCircleBot",
108 | "PathToCertificate": "",
109 | "BaseUrl": "https://example.com/bots/",
110 | "GameOptions": [ { "ShortName": "crazycircle" } ]
111 | }
112 | }
113 | ```
114 |
115 | ## Deploy
116 |
117 | Deploy your application and enjoy playing! 😃
--------------------------------------------------------------------------------
/docs/wiki/quick-start/echo-bot.md:
--------------------------------------------------------------------------------
1 | # Echo Bot 🤖
2 |
3 | This guide shows you how to quickly run your first bot using Visual Studio 2017 and ASP.NET Core.
4 |
5 | **Complete code is in [SampleEchoBot project](../../../sample/SampleEchoBot)**.
6 |
7 | This guide assumes that
8 |
9 | - You already have registered a bot and have its API Token.
10 |
11 | ## Project Setup
12 |
13 | Create a new ASP.NET Core (empty) version 1.1 or above app and add `Telegram.Bot.Framework` NuGet package to it.
14 |
15 | ## Code
16 |
17 | ### Bot
18 |
19 | Create your bot class:
20 |
21 | ```c#
22 | // EchoBot.cs
23 | public class EchoBot : BotBase {
24 | public EchoBot(IOptions> botOptions)
25 | : base(botOptions) { }
26 |
27 | public override Task HandleUnknownUpdate(Update update) => Task.CompletedTask;
28 |
29 | public override Task HandleFaultedUpdate(Update update, Exception e) => Task.CompletedTask;
30 | }
31 | ```
32 |
33 | ### Update Handlers
34 |
35 | Create an `/echo` command that echoes user input back:
36 |
37 | ```c#
38 | // EchoCommand.cs
39 | public class EchoCommandArgs : ICommandArgs {
40 | public string RawInput { get; set; }
41 | public string ArgsInput { get; set; }
42 | }
43 | public class EchoCommand : CommandBase {
44 | public EchoCommand() : base(name: "echo") {}
45 |
46 | public override async Task HandleCommand(Update update, EchoCommandArgs args) {
47 | string replyText = string.IsNullOrWhiteSpace(args.ArgsInput) ? "Echo What?" : args.ArgsInput;
48 |
49 | await Bot.Client.SendTextMessageAsync(
50 | update.Message.Chat.Id,
51 | replyText,
52 | replyToMessageId: update.Message.MessageId);
53 |
54 | return UpdateHandlingResult.Handled;
55 | }
56 | }
57 | ```
58 |
59 | > Pros only: Add a `/start` command as well that replies with text "Hello World!".
60 |
61 | ### Startup
62 |
63 | In `Startup` class, add the following code. This Adds Telegram bot and its update handlers to the DI
64 | container and also uses long-polling method to get new updates every 3 seconds. If you are running this
65 | app in a terminal, pressing Enter key will stop bot manager from getting updates.
66 |
67 | ```c#
68 | // Startup.cs
69 | private readonly IConfigurationRoot _configuration;
70 |
71 | public Startup(IHostingEnvironment env) {
72 | _configuration = new ConfigurationBuilder()
73 | .SetBasePath(env.ContentRootPath)
74 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
75 | .Build();
76 | }
77 |
78 | public void ConfigureServices(IServiceCollection services) {
79 | services.AddTelegramBot(_configuration.GetSection("EchoBot"))
80 | .AddUpdateHandler()
81 | .Configure();
82 | }
83 |
84 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
85 | var source = new CancellationTokenSource();
86 | Task.Factory.StartNew(() => {
87 | Console.WriteLine("## Press Enter to stop bot manager...");
88 | Console.ReadLine();
89 | source.Cancel();
90 | });
91 | Task.Factory.StartNew(async () => {
92 | var botManager = app.ApplicationServices.GetRequiredService>();
93 | while (!source.IsCancellationRequested) {
94 | await Task.Delay(3_000);
95 | await botManager.GetAndHandleNewUpdatesAsync();
96 | }
97 | Console.WriteLine("## Bot manager stopped.");
98 | Environment.Exit(0);
99 | }).ContinueWith(t => {
100 | if (t.IsFaulted) throw t.Exception;
101 | });
102 | }
103 | ```
104 |
105 | > Pros only: Add the `/start` command you created to the list of handlers as well.
106 |
107 | ## Configurations
108 |
109 | Add the following configurations to `appsettings.json` file in the project's root directory.
110 |
111 | ```json
112 | {
113 | "EchoBot": {
114 | "ApiToken": "your bot's api token",
115 | "BotUserName": "your bot's username without @"
116 | }
117 | }
118 | ```
119 |
120 | ## Run
121 |
122 | That's it! Run your the app and write to your bot: `/echo Hello`
123 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/App_Start/WebApiConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Http;
2 |
3 | namespace Quickstart.AspNet45
4 | {
5 | public static class WebApiConfig
6 | {
7 | public static void Register(HttpConfiguration config)
8 | {
9 | // Web API configuration and services
10 |
11 | // Web API routes
12 | config.MapHttpAttributeRoutes();
13 |
14 | config.Routes.MapHttpRoute(
15 | name: "DefaultApi",
16 | routeTemplate: "api/{controller}/{id}",
17 | defaults: new { id = RouteParameter.Optional }
18 | );
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Controllers/WebhookController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using System.Web.Http;
3 |
4 | namespace Quickstart.AspNet45.Controllers
5 | {
6 | public class WebhookController : ApiController
7 | {
8 | [HttpPost]
9 | public async Task Webhook()
10 | {
11 | await Task.Delay(1);
12 | //this.ActionContext.
13 | return this.BadRequest();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Global.asax:
--------------------------------------------------------------------------------
1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Quickstart.AspNet45.WebApiApplication" Language="C#" %>
2 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Global.asax.cs:
--------------------------------------------------------------------------------
1 | using System.Web;
2 | using System.Web.Http;
3 |
4 | namespace Quickstart.AspNet45
5 | {
6 | public class WebApiApplication : HttpApplication
7 | {
8 | protected void Application_Start()
9 | {
10 | GlobalConfiguration.Configure(WebApiConfig.Register);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Quickstart.AspNet45")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Quickstart.AspNet45")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("19b20342-5f00-4b54-b33a-7fd676590208")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Revision and Build Numbers
33 | // by using the '*' as shown below:
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Web.Debug.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Web.Release.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
30 |
31 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/Web.config:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 |
46 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNet45/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/EchoBot.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using Telegram.Bot.Framework;
3 |
4 | namespace Quickstart.AspNetCore
5 | {
6 | public class EchoBot : BotBase
7 | {
8 | public EchoBot(IOptions> options)
9 | : base(options.Value)
10 | {
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Extensions/AppStartupExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Logging;
3 | using Microsoft.Extensions.Options;
4 | using Quickstart.AspNetCore;
5 | using Quickstart.AspNetCore.Options;
6 | using Quickstart.AspNetCore.Services;
7 | using System;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using Telegram.Bot.Framework;
11 | using Telegram.Bot.Framework.Abstractions;
12 |
13 | namespace Microsoft.AspNetCore.Builder
14 | {
15 | static class AppStartupExtensions
16 | {
17 | public static IApplicationBuilder UseTelegramBotLongPolling(
18 | this IApplicationBuilder app,
19 | IBotBuilder botBuilder,
20 | TimeSpan startAfter = default,
21 | CancellationToken cancellationToken = default
22 | )
23 | where TBot : BotBase
24 | {
25 | if (startAfter == default)
26 | {
27 | startAfter = TimeSpan.FromSeconds(2);
28 | }
29 |
30 | var updateManager = new UpdatePollingManager(botBuilder, new BotServiceProvider(app));
31 |
32 | Task.Run(async () =>
33 | {
34 | await Task.Delay(startAfter, cancellationToken);
35 | await updateManager.RunAsync(cancellationToken: cancellationToken);
36 | }, cancellationToken)
37 | .ContinueWith(t =>
38 | {// ToDo use logger
39 | Console.ForegroundColor = ConsoleColor.Red;
40 | Console.WriteLine(t.Exception);
41 | Console.ResetColor();
42 | throw t.Exception;
43 | }, TaskContinuationOptions.OnlyOnFaulted);
44 |
45 | return app;
46 | }
47 |
48 | public static IApplicationBuilder EnsureWebhookSet(
49 | this IApplicationBuilder app
50 | )
51 | where TBot : IBot
52 | {
53 | using (var scope = app.ApplicationServices.CreateScope())
54 | {
55 | var logger = scope.ServiceProvider.GetRequiredService>();
56 | var bot = scope.ServiceProvider.GetRequiredService();
57 | var options = scope.ServiceProvider.GetRequiredService>>();
58 | var url = new Uri(new Uri(options.Value.WebhookDomain), options.Value.WebhookPath);
59 |
60 | logger.LogInformation("Setting webhook for bot \"{0}\" to URL \"{1}\"", typeof(TBot).Name, url);
61 |
62 | bot.Client.SetWebhookAsync(url.AbsoluteUri)
63 | .GetAwaiter().GetResult();
64 | }
65 |
66 | return app;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/CallbackQueryHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types;
4 |
5 | namespace Quickstart.AspNetCore.Handlers
6 | {
7 | public class CallbackQueryHandler : IUpdateHandler
8 | {
9 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | CallbackQuery cq = context.Update.CallbackQuery;
12 |
13 | await context.Bot.Client.AnswerCallbackQueryAsync(cq.Id, "PONG", showAlert: true);
14 |
15 | await next(context);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/Commands/PingCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types;
4 | using Telegram.Bot.Types.Enums;
5 | using Telegram.Bot.Types.ReplyMarkups;
6 |
7 | namespace Quickstart.AspNetCore.Handlers
8 | {
9 | class PingCommand : CommandBase
10 | {
11 | public override async Task HandleAsync(IUpdateContext context, UpdateDelegate next, string[] args)
12 | {
13 | Message msg = context.Update.Message;
14 |
15 | await context.Bot.Client.SendTextMessageAsync(
16 | msg.Chat,
17 | "*PONG*",
18 | ParseMode.Markdown,
19 | replyToMessageId: msg.MessageId,
20 | replyMarkup: new InlineKeyboardMarkup(
21 | InlineKeyboardButton.WithCallbackData("Ping", "PONG")
22 | )
23 | );
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/Commands/StartCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 |
4 | namespace Quickstart.AspNetCore.Handlers
5 | {
6 | class StartCommand : CommandBase
7 | {
8 | public override async Task HandleAsync(IUpdateContext context, UpdateDelegate next, string[] args)
9 | {
10 | await context.Bot.Client.SendTextMessageAsync(context.Update.Message.Chat, "Hello, World!");
11 | await next(context);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/ExceptionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Quickstart.AspNetCore.Handlers
6 | {
7 | public class ExceptionHandler : IUpdateHandler
8 | {
9 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | var u = context.Update;
12 |
13 | try
14 | {
15 | await next(context);
16 | }
17 | catch (Exception e)
18 | {
19 | Console.BackgroundColor = ConsoleColor.Black;
20 | Console.ForegroundColor = ConsoleColor.Red;
21 | Console.WriteLine("An error occured in handling update {0}.{1}{2}", u.Id, Environment.NewLine, e);
22 | Console.ResetColor();
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/StickerHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 |
6 | namespace Quickstart.AspNetCore.Handlers
7 | {
8 | class StickerHandler : IUpdateHandler
9 | {
10 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
11 | {
12 | Message msg = context.Update.Message;
13 | Sticker incomingSticker = msg.Sticker;
14 |
15 | StickerSet evilMindsSet = await context.Bot.Client.GetStickerSetAsync("EvilMinds");
16 |
17 | Sticker similarEvilMindSticker = evilMindsSet.Stickers.FirstOrDefault(
18 | sticker => incomingSticker.Emoji.Contains(sticker.Emoji)
19 | );
20 |
21 | Sticker replySticker = similarEvilMindSticker ?? evilMindsSet.Stickers.First();
22 |
23 | await context.Bot.Client.SendStickerAsync(
24 | msg.Chat,
25 | replySticker.FileId,
26 | replyToMessageId: msg.MessageId
27 | );
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/TextEchoer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types;
4 |
5 | namespace Quickstart.AspNetCore.Handlers
6 | {
7 | public class TextEchoer : IUpdateHandler
8 | {
9 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | Message msg = context.Update.Message;
12 |
13 | await context.Bot.Client.SendTextMessageAsync(
14 | msg.Chat, "You said:\n" + msg.Text
15 | );
16 |
17 | await next(context);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/UpdateMembersList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Quickstart.AspNetCore.Handlers
6 | {
7 | class UpdateMembersList : IUpdateHandler
8 | {
9 | public Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | Console.BackgroundColor = ConsoleColor.Black;
12 | Console.ForegroundColor = ConsoleColor.Yellow;
13 | Console.WriteLine("Updating chat members list...");
14 | Console.ResetColor();
15 |
16 | return next(context);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/WeatherReporter.cs:
--------------------------------------------------------------------------------
1 | using Quickstart.AspNetCore.Services;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 | using Telegram.Bot.Types.Enums;
6 |
7 | namespace Quickstart.AspNetCore.Handlers
8 | {
9 | class WeatherReporter : IUpdateHandler
10 | {
11 | private readonly IWeatherService _weatherService;
12 |
13 | public WeatherReporter(IWeatherService weatherService)
14 | {
15 | _weatherService = weatherService;
16 | }
17 |
18 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
19 | {
20 | Message msg = context.Update.Message;
21 | Location location = msg.Location;
22 |
23 | var weather = await _weatherService.GetWeatherAsync(location.Latitude, location.Longitude);
24 |
25 | await context.Bot.Client.SendTextMessageAsync(
26 | msg.Chat,
27 | $"Weather status is *{weather.Status}* with the temperature of {weather.Temp:F1}.\n" +
28 | $"Min: {weather.MinTemp:F1}\n" +
29 | $"Max: {weather.MaxTemp:F1}\n\n\n" +
30 | "powered by [MetaWeather](https://www.metaweather.com)",
31 | ParseMode.Markdown,
32 | replyToMessageId: msg.MessageId
33 | );
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Handlers/WebhookLogger.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.Logging;
3 | using System.Threading.Tasks;
4 | using Telegram.Bot.Framework.Abstractions;
5 |
6 | namespace Quickstart.AspNetCore.Handlers
7 | {
8 | class WebhookLogger : IUpdateHandler
9 | {
10 | private readonly ILogger _logger;
11 |
12 | public WebhookLogger(
13 | ILogger logger
14 | )
15 | {
16 | _logger = logger;
17 | }
18 |
19 | public Task HandleAsync(IUpdateContext context, UpdateDelegate next)
20 | {
21 | var httpContext = (HttpContext)context.Items[nameof(HttpContext)];
22 |
23 | _logger.LogInformation(
24 | "Received update {0} in a webhook at {1}.",
25 | context.Update.Id,
26 | httpContext.Request.Host
27 | );
28 |
29 | return next(context);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Options/CustomBotOptions.cs:
--------------------------------------------------------------------------------
1 | using Telegram.Bot.Framework;
2 | using Telegram.Bot.Framework.Abstractions;
3 |
4 | namespace Quickstart.AspNetCore.Options
5 | {
6 | public class CustomBotOptions : BotOptions
7 | where TBot : IBot
8 | {
9 | public string WebhookDomain { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 |
5 | namespace Quickstart.AspNetCore
6 | {
7 | public class Program
8 | {
9 | public static void Main(string[] args)
10 | {
11 | CreateWebHostBuilder(args).Build().Run();
12 | }
13 |
14 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
15 | WebHost.CreateDefaultBuilder(args)
16 | .ConfigureAppConfiguration((hostBuilder, configBuilder) => configBuilder
17 | .AddJsonFile("appsettings.json")
18 | .AddJsonFile($"appsettings.{hostBuilder.HostingEnvironment.EnvironmentName}.json", true)
19 | .AddJsonEnvVar("QUICKSTART_SETTINGS", true)
20 | ).UseStartup();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:51740",
7 | "sslPort": 44394
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "environmentVariables": {
14 | "ASPNETCORE_ENVIRONMENT": "Development"
15 | }
16 | },
17 | "Quickstart.AspNetCore": {
18 | "commandName": "Project",
19 | "environmentVariables": {
20 | "ASPNETCORE_ENVIRONMENT": "Development"
21 | },
22 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
23 | },
24 | "Docker": {
25 | "commandName": "Docker",
26 | "launchBrowser": true,
27 | "launchUrl": "{Scheme}://localhost:{ServicePort}"
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Quickstart.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Services/BotServiceProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using System;
4 | using Telegram.Bot.Framework.Abstractions;
5 |
6 | namespace Quickstart.AspNetCore.Services
7 | {
8 | internal class BotServiceProvider : IBotServiceProvider
9 | {
10 | private readonly IServiceProvider _container;
11 |
12 | private readonly IServiceScope _scope;
13 |
14 | public BotServiceProvider(IApplicationBuilder app)
15 | {
16 | _container = app.ApplicationServices;
17 | }
18 |
19 | public BotServiceProvider(IServiceScope scope)
20 | {
21 | _scope = scope;
22 | }
23 |
24 | public object GetService(Type serviceType) =>
25 | _scope != null
26 | ? _scope.ServiceProvider.GetService(serviceType)
27 | : _container.GetService(serviceType)
28 | ;
29 |
30 | public IBotServiceProvider CreateScope() =>
31 | new BotServiceProvider(_container.CreateScope());
32 |
33 | public void Dispose()
34 | {
35 | _scope?.Dispose();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Services/CurrentWeather.cs:
--------------------------------------------------------------------------------
1 | namespace Quickstart.AspNetCore.Services
2 | {
3 | struct CurrentWeather
4 | {
5 | public string Status;
6 | public float Temp;
7 | public float MinTemp;
8 | public float MaxTemp;
9 | }
10 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Services/IWeatherService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Quickstart.AspNetCore.Services
4 | {
5 | interface IWeatherService
6 | {
7 | Task GetWeatherAsync(float lat, float lon);
8 | }
9 | }
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Services/WeatherService.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 |
6 | namespace Quickstart.AspNetCore.Services
7 | {
8 | class WeatherService : IWeatherService
9 | {
10 | private readonly HttpClient _client;
11 |
12 | public WeatherService()
13 | {
14 | _client = new HttpClient
15 | {
16 | BaseAddress = new Uri("https://www.metaweather.com/api/")
17 | };
18 | }
19 |
20 | public async Task GetWeatherAsync(float lat, float lon)
21 | {
22 | string location = await FindLocationIdAsync(lat, lon)
23 | .ConfigureAwait(false);
24 |
25 | DateTime today = DateTime.Today;
26 |
27 | string json = await _client.GetStringAsync($"location/{location}/{today.Year}/{today.Month}/{today.Day}")
28 | .ConfigureAwait(false);
29 |
30 | dynamic arr = JsonConvert.DeserializeObject(json);
31 |
32 | return new CurrentWeather
33 | {
34 | Status = arr[0].weather_state_name,
35 | Temp = arr[0].the_temp,
36 | MinTemp = arr[0].min_temp,
37 | MaxTemp = arr[0].max_temp,
38 | };
39 | }
40 |
41 | private async Task FindLocationIdAsync(float lat, float lon)
42 | {
43 | string json = await _client.GetStringAsync($"location/search?lattlong={lat},{lon}")
44 | .ConfigureAwait(false);
45 | dynamic arr = JsonConvert.DeserializeObject(json);
46 | return arr[0].woeid;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Quickstart.AspNetCore.Handlers;
7 | using Quickstart.AspNetCore.Options;
8 | using Quickstart.AspNetCore.Services;
9 | using System;
10 | using Telegram.Bot.Framework;
11 | using Telegram.Bot.Framework.Abstractions;
12 |
13 | namespace Quickstart.AspNetCore
14 | {
15 | public class Startup
16 | {
17 | private IConfiguration Configuration { get; }
18 |
19 | public Startup(IConfiguration configuration)
20 | {
21 | Configuration = configuration;
22 | }
23 |
24 | public void ConfigureServices(IServiceCollection services)
25 | {
26 | services.AddTransient()
27 | .Configure>(Configuration.GetSection("EchoBot"))
28 | .Configure>(Configuration.GetSection("EchoBot"))
29 | .AddScoped()
30 | .AddScoped()
31 | .AddScoped()
32 | .AddScoped()
33 | .AddScoped()
34 | .AddScoped()
35 | .AddScoped()
36 | .AddScoped()
37 | .AddScoped()
38 | ;
39 | services.AddScoped();
40 | }
41 |
42 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
43 | {
44 | if (env.IsDevelopment())
45 | {
46 | app.UseDeveloperExceptionPage();
47 |
48 | // get bot updates from Telegram via long-polling approach during development
49 | // this will disable Telegram webhooks
50 | app.UseTelegramBotLongPolling(ConfigureBot(), startAfter: TimeSpan.FromSeconds(2));
51 | }
52 | else
53 | {
54 | // use Telegram bot webhook middleware in higher environments
55 | app.UseTelegramBotWebhook(ConfigureBot());
56 | // and make sure webhook is enabled
57 | app.EnsureWebhookSet();
58 | }
59 |
60 | app.Run(async context =>
61 | {
62 | await context.Response.WriteAsync("Hello World!");
63 | });
64 | }
65 |
66 | private IBotBuilder ConfigureBot()
67 | {
68 | return new BotBuilder()
69 | .Use()
70 |
71 | // .Use()
72 | .UseWhen(When.Webhook)
73 |
74 | .UseWhen(When.MembersChanged)
75 |
76 | .MapWhen(When.NewMessage, msgBranch => msgBranch
77 | .MapWhen(When.NewTextMessage, txtBranch => txtBranch
78 | .Use()
79 | .MapWhen(When.NewCommand, cmdBranch => cmdBranch
80 | .UseCommand("ping")
81 | .UseCommand("start")
82 | )
83 | //.Use()
84 | )
85 | .MapWhen(When.StickerMessage)
86 | .MapWhen(When.LocationMessage)
87 | )
88 |
89 | .MapWhen(When.CallbackQuery)
90 |
91 | // .Use()
92 | ;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/When.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System.Linq;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types.Enums;
5 |
6 | namespace Quickstart.AspNetCore
7 | {
8 | public static class When
9 | {
10 | public static bool Webhook(IUpdateContext context)
11 | => context.Items.ContainsKey(nameof(HttpContext));
12 |
13 | public static bool NewMessage(IUpdateContext context) =>
14 | context.Update.Message != null;
15 |
16 | public static bool NewTextMessage(IUpdateContext context) =>
17 | context.Update.Message?.Text != null;
18 |
19 | public static bool NewCommand(IUpdateContext context) =>
20 | context.Update.Message?.Entities?.FirstOrDefault()?.Type == MessageEntityType.BotCommand;
21 |
22 | public static bool MembersChanged(IUpdateContext context) =>
23 | context.Update.Message?.NewChatMembers != null ||
24 | context.Update.Message?.LeftChatMember != null ||
25 | context.Update.ChannelPost?.NewChatMembers != null ||
26 | context.Update.ChannelPost?.LeftChatMember != null
27 | ;
28 |
29 | public static bool LocationMessage(IUpdateContext context) =>
30 | context.Update.Message?.Location != null;
31 |
32 | public static bool StickerMessage(IUpdateContext context) =>
33 | context.Update.Message?.Sticker != null;
34 |
35 | public static bool CallbackQuery(IUpdateContext context) =>
36 | context.Update.CallbackQuery != null;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/sample/Quickstart.AspNetCore/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "EchoBot": {
3 | "Username": "quickstart_tgbot",
4 | "ApiToken": "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy",
5 | "WebhookDomain": "https://quickstart-tgbot.herokuapp.com",
6 | "WebhookPath": "/api/bots/1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy/webhook"
7 | }
8 | }
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/EchoBot.cs:
--------------------------------------------------------------------------------
1 | using Telegram.Bot.Framework;
2 |
3 | namespace Quickstart.Net45
4 | {
5 | class EchoBot : BotBase
6 | {
7 | public EchoBot(string apiToken)
8 | : base(username: "quickstart_tgbot", token: apiToken)
9 | {
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/CallbackQueryHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types;
4 |
5 | namespace Quickstart.Net45.Handlers
6 | {
7 | public class CallbackQueryHandler : IUpdateHandler
8 | {
9 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | CallbackQuery cq = context.Update.CallbackQuery;
12 |
13 | await context.Bot.Client.AnswerCallbackQueryAsync(cq.Id, "PONG", showAlert: true);
14 |
15 | await next(context);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/Commands/PingCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types;
4 | using Telegram.Bot.Types.Enums;
5 | using Telegram.Bot.Types.ReplyMarkups;
6 |
7 | namespace Quickstart.Net45.Handlers
8 | {
9 | class PingCommand : CommandBase
10 | {
11 | public override async Task HandleAsync(IUpdateContext context, UpdateDelegate next, string[] args)
12 | {
13 | Message msg = context.Update.Message;
14 |
15 | await context.Bot.Client.SendTextMessageAsync(
16 | msg.Chat,
17 | "*PONG*",
18 | ParseMode.Markdown,
19 | replyToMessageId: msg.MessageId,
20 | replyMarkup: new InlineKeyboardMarkup(
21 | InlineKeyboardButton.WithCallbackData("Ping", "PONG")
22 | )
23 | );
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/Commands/StartCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 |
4 | namespace Quickstart.Net45.Handlers
5 | {
6 | class StartCommand : CommandBase
7 | {
8 | public override async Task HandleAsync(IUpdateContext context, UpdateDelegate next, string[] args)
9 | {
10 | await context.Bot.Client.SendTextMessageAsync(context.Update.Message.Chat, "Hello, World!");
11 | await next(context);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/ExceptionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Quickstart.Net45.Handlers
6 | {
7 | public class ExceptionHandler : IUpdateHandler
8 | {
9 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | var u = context.Update;
12 |
13 | try
14 | {
15 | await next(context);
16 | }
17 | catch (Exception e)
18 | {
19 | Console.BackgroundColor = ConsoleColor.Black;
20 | Console.ForegroundColor = ConsoleColor.Red;
21 | Console.WriteLine("An error occured in handling update {0}.{1}{2}", u.Id, Environment.NewLine, e);
22 | Console.ResetColor();
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/StickerHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 |
6 | namespace Quickstart.Net45.Handlers
7 | {
8 | class StickerHandler : IUpdateHandler
9 | {
10 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
11 | {
12 | Message msg = context.Update.Message;
13 | Sticker incomingSticker = msg.Sticker;
14 |
15 | StickerSet evilMindsSet = await context.Bot.Client.GetStickerSetAsync("EvilMinds");
16 |
17 | Sticker similarEvilMindSticker = evilMindsSet.Stickers.FirstOrDefault(
18 | sticker => incomingSticker.Emoji.Contains(sticker.Emoji)
19 | );
20 |
21 | Sticker replySticker = similarEvilMindSticker ?? evilMindsSet.Stickers.First();
22 |
23 | await context.Bot.Client.SendStickerAsync(
24 | msg.Chat,
25 | replySticker.FileId,
26 | replyToMessageId: msg.MessageId
27 | );
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/TextEchoer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types;
4 |
5 | namespace Quickstart.Net45.Handlers
6 | {
7 | public class TextEchoer : IUpdateHandler
8 | {
9 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | Message msg = context.Update.Message;
12 |
13 | await context.Bot.Client.SendTextMessageAsync(
14 | msg.Chat, "You said:\n" + msg.Text
15 | );
16 |
17 | await next(context);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/UpdateMembersList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Quickstart.Net45.Handlers
6 | {
7 | class UpdateMembersList : IUpdateHandler
8 | {
9 | public Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | Console.BackgroundColor = ConsoleColor.Black;
12 | Console.ForegroundColor = ConsoleColor.Yellow;
13 | Console.WriteLine("Updating chat members list...");
14 | Console.ResetColor();
15 |
16 | return next(context);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/WeatherReporter.cs:
--------------------------------------------------------------------------------
1 | using Quickstart.Net45.Services;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 | using Telegram.Bot.Types.Enums;
6 |
7 | namespace Quickstart.Net45.Handlers
8 | {
9 | class WeatherReporter : IUpdateHandler
10 | {
11 | private readonly IWeatherService _weatherService;
12 |
13 | public WeatherReporter(IWeatherService weatherService)
14 | {
15 | _weatherService = weatherService;
16 | }
17 |
18 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
19 | {
20 | Message msg = context.Update.Message;
21 | Location location = msg.Location;
22 |
23 | var weather = await _weatherService.GetWeatherAsync(location.Latitude, location.Longitude);
24 |
25 | await context.Bot.Client.SendTextMessageAsync(
26 | msg.Chat,
27 | $"Weather status is *{weather.Status}* with the temperature of {weather.Temp:F1}.\n" +
28 | $"Min: {weather.MinTemp:F1}\n" +
29 | $"Max: {weather.MaxTemp:F1}\n\n\n" +
30 | "powered by [MetaWeather](https://www.metaweather.com)",
31 | ParseMode.Markdown,
32 | replyToMessageId: msg.MessageId
33 | );
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Handlers/WebhookLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Quickstart.Net45.Handlers
6 | {
7 | class WebhookLogger : IUpdateHandler
8 | {
9 | public Task HandleAsync(IUpdateContext context, UpdateDelegate next)
10 | {
11 | Console.BackgroundColor = ConsoleColor.Black;
12 | Console.ForegroundColor = ConsoleColor.Yellow;
13 | Console.WriteLine("Received update {0} as a webhook.", context.Update.Id);
14 | Console.ResetColor();
15 |
16 | return next(context);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Program.cs:
--------------------------------------------------------------------------------
1 | using Quickstart.Net45.Handlers;
2 | using Quickstart.Net45.Services;
3 | using Quickstart.Net45.Services.SimpleInjector;
4 | using SimpleInjector;
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Telegram.Bot.Framework;
9 | using Telegram.Bot.Framework.Abstractions;
10 |
11 | namespace Quickstart.Net45
12 | {
13 | class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | string token = Environment.GetEnvironmentVariable("BOT_API_TOKEN") ?? "YOUR_API_TOKEN_HERE";
18 |
19 | var container = new Container();
20 | container.Register(() => new EchoBot(token));
21 | container.Register();
22 | container.Register();
23 | container.Verify();
24 |
25 | var mgr = new UpdatePollingManager(ConfigureBot(), new BotServiceProvider(container));
26 |
27 | var tokenSrc = new CancellationTokenSource();
28 | Task.Run(() =>
29 | {
30 | Console.ReadLine();
31 | tokenSrc.Cancel();
32 | });
33 |
34 | mgr.RunAsync(cancellationToken: tokenSrc.Token).GetAwaiter().GetResult();
35 | }
36 |
37 | static IBotBuilder ConfigureBot()
38 | {
39 | return new BotBuilder()
40 | .Use()
41 |
42 | // .Use()
43 | .UseWhen(When.Webhook)
44 |
45 | .UseWhen(When.MembersChanged)
46 |
47 | .MapWhen(When.NewMessage, msgBranch => msgBranch
48 | .MapWhen(When.NewTextMessage, txtBranch => txtBranch
49 | .Use()
50 | .MapWhen(When.NewCommand, cmdBranch => cmdBranch
51 | .UseCommand("ping")
52 | .UseCommand("start")
53 | )
54 | //.Use()
55 | )
56 | .MapWhen(When.StickerMessage, branch => branch.Use())
57 | .MapWhen(When.LocationMessage, branch => branch.Use())
58 | )
59 |
60 | .MapWhen(When.CallbackQuery)
61 |
62 | // .Use()
63 | ;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Quickstart.Net45")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Quickstart.Net45")]
12 | [assembly: AssemblyCopyright("Copyright © 2018")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("C3672842-82EE-49F3-90A1-0F07A7857EBE")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Quickstart.Net45.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {C3672842-82EE-49F3-90A1-0F07A7857EBE}
8 | Exe
9 | Properties
10 | Quickstart.Net45
11 | Quickstart.Net45
12 | v4.5
13 | 512
14 | latest
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 | ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
39 | True
40 |
41 |
42 | ..\..\packages\SimpleInjector.4.3.0\lib\net45\SimpleInjector.dll
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | ..\..\packages\Telegram.Bot.14.10.0\lib\net45\Telegram.Bot.dll
51 | True
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 | {ec991578-d67e-4208-b540-7fbb603b7d8f}
77 | Telegram.Bot.Framework
78 |
79 |
80 |
81 |
88 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Services/SimpleInjector/BotServiceProvider.cs:
--------------------------------------------------------------------------------
1 | using SimpleInjector;
2 | using SimpleInjector.Lifestyles;
3 | using System;
4 | using Telegram.Bot.Framework.Abstractions;
5 |
6 | namespace Quickstart.Net45.Services.SimpleInjector
7 | {
8 | class BotServiceProvider : IBotServiceProvider
9 | {
10 | private readonly Container _container;
11 |
12 | private readonly Scope _scope;
13 |
14 | public BotServiceProvider(Container container)
15 | {
16 | _container = container;
17 | }
18 |
19 | private BotServiceProvider(Scope scope)
20 | {
21 | _scope = scope;
22 | }
23 |
24 | public object GetService(Type serviceType) =>
25 | _scope != null
26 | ? _scope.GetInstance(serviceType)
27 | : _container.GetInstance(serviceType)
28 | ;
29 |
30 | public IBotServiceProvider CreateScope() =>
31 | new BotServiceProvider(ThreadScopedLifestyle.BeginScope(_container));
32 |
33 | public void Dispose()
34 | {
35 | _scope?.Dispose();
36 | _container?.Dispose();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/Services/WeatherService.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 |
6 | namespace Quickstart.Net45.Services
7 | {
8 | interface IWeatherService
9 | {
10 | Task GetWeatherAsync(float lat, float lon);
11 | }
12 |
13 | struct CurrentWeather
14 | {
15 | public string Status;
16 | public float Temp;
17 | public float MinTemp;
18 | public float MaxTemp;
19 | }
20 |
21 | class WeatherService : IWeatherService
22 | {
23 | private readonly HttpClient _client;
24 |
25 | public WeatherService()
26 | {
27 | _client = new HttpClient
28 | {
29 | BaseAddress = new Uri("https://www.metaweather.com/api/")
30 | };
31 | }
32 |
33 | public async Task GetWeatherAsync(float lat, float lon)
34 | {
35 | string location = await FindLocationIdAsync(lat, lon)
36 | .ConfigureAwait(false);
37 |
38 | DateTime today = DateTime.Today;
39 |
40 | string json = await _client.GetStringAsync($"location/{location}/{today.Year}/{today.Month}/{today.Day}")
41 | .ConfigureAwait(false);
42 |
43 | dynamic arr = JsonConvert.DeserializeObject(json);
44 |
45 | return new CurrentWeather
46 | {
47 | Status = arr[0].weather_state_name,
48 | Temp = arr[0].the_temp,
49 | MinTemp = arr[0].min_temp,
50 | MaxTemp = arr[0].max_temp,
51 | };
52 | }
53 |
54 | private async Task FindLocationIdAsync(float lat, float lon)
55 | {
56 | string json = await _client.GetStringAsync($"location/search?lattlong={lat},{lon}")
57 | .ConfigureAwait(false);
58 | dynamic arr = JsonConvert.DeserializeObject(json);
59 | return arr[0].woeid;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/When.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Types.Enums;
4 |
5 | namespace Quickstart.Net45
6 | {
7 | public static class When
8 | {
9 | public static bool Webhook(IUpdateContext context)
10 | => context.Items.ContainsKey("HttpContext");
11 |
12 | public static bool NewMessage(IUpdateContext context) =>
13 | context.Update.Message != null;
14 |
15 | public static bool NewTextMessage(IUpdateContext context) =>
16 | context.Update.Message?.Text != null;
17 |
18 | public static bool NewCommand(IUpdateContext context) =>
19 | context.Update.Message?.Entities?.FirstOrDefault()?.Type == MessageEntityType.BotCommand;
20 |
21 | public static bool MembersChanged(IUpdateContext context) =>
22 | context.Update.Message?.NewChatMembers != null ||
23 | context.Update.Message?.LeftChatMember != null ||
24 | context.Update.ChannelPost?.NewChatMembers != null ||
25 | context.Update.ChannelPost?.LeftChatMember != null
26 | ;
27 |
28 | public static bool LocationMessage(IUpdateContext context) =>
29 | context.Update.Message?.Location != null;
30 |
31 | public static bool StickerMessage(IUpdateContext context) =>
32 | context.Update.Message?.Sticker != null;
33 |
34 | public static bool CallbackQuery(IUpdateContext context) =>
35 | context.Update.CallbackQuery != null;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/sample/Quickstart.Net45/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/SampleBots/BotUpdateGetterTask.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Microsoft.Extensions.Logging;
4 | using RecurrentTasks;
5 | using Telegram.Bot.Framework.Abstractions;
6 |
7 | namespace SampleBots
8 | {
9 | public class BotUpdateGetterTask : IRunnable
10 | where TBot : class, IBot
11 | {
12 | private readonly IBotManager _botManager;
13 |
14 | private readonly ILogger _logger;
15 |
16 | public BotUpdateGetterTask(IBotManager botManager, ILogger> logger)
17 | {
18 | _botManager = botManager;
19 | _logger = logger;
20 | }
21 |
22 | public void Run(ITask currentTask, CancellationToken cancellationToken)
23 | {
24 | Task.Factory.StartNew(async () =>
25 | {
26 | _logger.LogTrace($"{typeof(TBot).Name}: Checking for updates...");
27 | await _botManager.GetAndHandleNewUpdatesAsync();
28 | _logger.LogTrace($"{typeof(TBot).Name}: Handling updates finished");
29 | }, cancellationToken).ContinueWith(task =>
30 | {
31 | if (task.IsFaulted)
32 | {
33 | throw task.Exception.InnerException;
34 | }
35 | }, cancellationToken);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/sample/SampleBots/Bots/EchoBot/EchoerBot.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.Extensions.Logging;
4 | using Microsoft.Extensions.Options;
5 | using Telegram.Bot.Framework;
6 | using Telegram.Bot.Types;
7 | using Telegram.Bot.Types.Enums;
8 |
9 | namespace SampleBots.Bots.EchoBot
10 | {
11 | public class EchoerBot : BotBase
12 | {
13 | private readonly ILogger _logger;
14 |
15 | public EchoerBot(IOptions> botOptions, ILogger logger)
16 | : base(botOptions)
17 | {
18 | _logger = logger;
19 | }
20 |
21 | public override async Task HandleUnknownUpdate(Update update)
22 | {
23 | _logger.LogWarning("Unable to handle an update");
24 |
25 | const string unknownUpdateText = "Sorry! I don't know what to do with this message";
26 |
27 | if (update.Type == UpdateType.Message)
28 | {
29 | await Client.SendTextMessageAsync(update.Message.Chat.Id,
30 | unknownUpdateText,
31 | replyToMessageId: update.Message.MessageId);
32 | }
33 | else
34 | {
35 |
36 | }
37 | }
38 |
39 | public override Task HandleFaultedUpdate(Update update, Exception e)
40 | {
41 | _logger.LogCritical("Exception thrown while handling an update");
42 | return Task.CompletedTask;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/sample/SampleBots/Bots/EchoBot/TextMessageEchoer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 | using Telegram.Bot.Types.Enums;
6 |
7 | namespace SampleBots.Bots.EchoBot
8 | {
9 | public class TextMessageEchoer : UpdateHandlerBase
10 | {
11 | public override bool CanHandleUpdate(IBot bot, Update update)
12 | {
13 | return !string.IsNullOrEmpty(update.Message?.Text);
14 | }
15 |
16 | public override async Task HandleUpdateAsync(IBot bot, Update update)
17 | {
18 | string replyText = $"You said:\n`{update.Message.Text.Replace("\n", "`\n`")}`";
19 |
20 | await bot.Client.SendTextMessageAsync(
21 | update.Message.Chat.Id,
22 | replyText,
23 | ParseMode.Markdown,
24 | replyToMessageId: update.Message.MessageId);
25 |
26 | return UpdateHandlingResult.Continue;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/sample/SampleBots/Bots/GreeterBot/GreeterBot.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.Extensions.Logging;
4 | using Microsoft.Extensions.Options;
5 | using Telegram.Bot.Framework;
6 | using Telegram.Bot.Types;
7 | using Telegram.Bot.Types.Enums;
8 |
9 | namespace SampleBots.Bots.GreeterBot
10 | {
11 | public class GreeterBot : BotBase
12 | {
13 | private readonly ILogger _logger;
14 |
15 | public GreeterBot(IOptions> botOptions, ILogger logger)
16 | : base(botOptions)
17 | {
18 | _logger = logger;
19 | }
20 |
21 | public override async Task HandleUnknownUpdate(Update update)
22 | {
23 | _logger.LogWarning("Unable to handle an update");
24 |
25 | const string unknownUpdateText = "Sorry! I don't know what to do with this message";
26 |
27 | if (update.Type == UpdateType.Message)
28 | {
29 | await Client.SendTextMessageAsync(update.Message.Chat.Id,
30 | unknownUpdateText,
31 | replyToMessageId: update.Message.MessageId);
32 | }
33 | else
34 | {
35 |
36 | }
37 | }
38 |
39 | public override Task HandleFaultedUpdate(Update update, Exception e)
40 | {
41 | _logger.LogCritical("Exception thrown while handling an update");
42 | return Task.CompletedTask;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/sample/SampleBots/Bots/GreeterBot/HiCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text.RegularExpressions;
3 | using System.Threading.Tasks;
4 | using Telegram.Bot.Framework;
5 | using Telegram.Bot.Framework.Abstractions;
6 | using Telegram.Bot.Types;
7 | using Telegram.Bot.Types.Enums;
8 |
9 | namespace SampleBots.Bots.GreeterBot
10 | {
11 | public class HiCommandArgs : ICommandArgs
12 | {
13 | public string RawInput { get; set; }
14 |
15 | public string ArgsInput { get; set; }
16 |
17 | public string PersonName { get; set; }
18 | }
19 |
20 | public class HiCommand : CommandBase
21 | {
22 | private const string CommandName = "hi";
23 |
24 | private const string HiMessageFormat = "Hello, *{0}*!";
25 |
26 | private const string HelpText = "Here is a tip to use this command:\n" +
27 | "```\n" +
28 | "/hi John" +
29 | "```";
30 |
31 | public HiCommand()
32 | : base(CommandName)
33 | {
34 |
35 | }
36 |
37 | protected override HiCommandArgs ParseInput(Update update)
38 | {
39 | var tokens = Regex.Split(update.Message.Text.Trim(), @"\s+");
40 | var args = base.ParseInput(update);
41 |
42 | if (tokens.Length > 1)
43 | {
44 | args.PersonName = string.Join(" ", tokens.Skip(1));
45 | }
46 |
47 | return args;
48 | }
49 |
50 | public override async Task HandleCommand(Update update, HiCommandArgs args)
51 | {
52 | string text;
53 | if (args.PersonName is null)
54 | {
55 | text = HelpText;
56 | }
57 | else
58 | {
59 | text = string.Format(HiMessageFormat, args.PersonName);
60 | }
61 |
62 | await Bot.Client.SendTextMessageAsync(
63 | update.Message.Chat.Id,
64 | text,
65 | ParseMode.Markdown,
66 | replyToMessageId: update.Message.MessageId
67 | );
68 |
69 | return UpdateHandlingResult.Continue;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/sample/SampleBots/Bots/GreeterBot/PhotoForwarder.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 |
6 | namespace SampleBots.Bots.GreeterBot
7 | {
8 | public class PhotoForwarder : UpdateHandlerBase
9 | {
10 | public override bool CanHandleUpdate(IBot bot, Update update)
11 | {
12 | return update.Message?.Photo != null;
13 | }
14 |
15 | public override async Task HandleUpdateAsync(IBot bot, Update update)
16 | {
17 | await bot.Client.ForwardMessageAsync(update.Message.Chat.Id,
18 | update.Message.Chat.Id,
19 | update.Message.MessageId);
20 |
21 | return UpdateHandlingResult.Handled;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/sample/SampleBots/Bots/GreeterBot/StartCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 | using Telegram.Bot.Types.Enums;
6 |
7 | namespace SampleBots.Bots.GreeterBot
8 | {
9 | public class StartCommandArgs : ICommandArgs
10 | {
11 | public string RawInput { get; set; }
12 |
13 | public string ArgsInput { get; set; }
14 | }
15 |
16 | public class StartCommand : CommandBase
17 | {
18 | private const string CommandName = "start";
19 |
20 | private const string StartMessageFormat = "Hey *{0}*!\n\n" +
21 | "Try my _hi_ command like this:\n" +
22 | "`/hi John`\n\n" +
23 | "By the way, I _forward back any photo you send_ here ;)";
24 |
25 | public StartCommand()
26 | : base(CommandName)
27 | {
28 |
29 | }
30 |
31 | public override async Task HandleCommand(Update update, StartCommandArgs args)
32 | {
33 | await Bot.Client.SendTextMessageAsync(update.Message.Chat.Id,
34 | string.Format(StartMessageFormat, update.Message.From.FirstName),
35 | ParseMode.Markdown,
36 | replyToMessageId: update.Message.ForwardFromMessageId);
37 |
38 | return UpdateHandlingResult.Handled;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/sample/SampleBots/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.Configuration;
5 |
6 | namespace SampleBots
7 | {
8 | public class Program
9 | {
10 | public static void Main(string[] args)
11 | {
12 | Console.Title = "Telegram.Bot.Framework - Sample Bots";
13 |
14 | var config = new ConfigurationBuilder()
15 | .AddCommandLine(args)
16 | .Build();
17 |
18 | var host = new WebHostBuilder()
19 | .UseKestrel()
20 | .UseContentRoot(Directory.GetCurrentDirectory())
21 | .UseIISIntegration()
22 | .UseConfiguration(config)
23 | .UseStartup()
24 | .UseApplicationInsights()
25 | .Build();
26 |
27 | host.Run();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/SampleBots/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "anonymousAuthentication": true,
4 | "iisExpress": {
5 | "applicationUrl": "http://localhost:53136/",
6 | "sslPort": 0
7 | },
8 | "windowsAuthentication": false
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "environmentVariables": {
14 | "ASPNETCORE_ENVIRONMENT": "Development"
15 | }
16 | },
17 | "SampleBots": {
18 | "applicationUrl": "http://localhost:53137",
19 | "commandName": "Project",
20 | "environmentVariables": {
21 | "ASPNETCORE_ENVIRONMENT": "Development"
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/sample/SampleBots/SampleBots.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp1.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/sample/SampleBots/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.Extensions.Logging;
9 | using RecurrentTasks;
10 | using SampleBots.Bots.EchoBot;
11 | using SampleBots.Bots.GreeterBot;
12 | using Telegram.Bot.Framework;
13 |
14 | namespace SampleBots
15 | {
16 | public class Startup
17 | {
18 | public IConfigurationRoot Configuration { get; }
19 |
20 | public Startup(IHostingEnvironment env)
21 | {
22 | Configuration = new ConfigurationBuilder()
23 | .SetBasePath(env.ContentRootPath)
24 | .AddJsonFile("appsettings.json")
25 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
26 | .AddEnvironmentVariables("SampleEchoBot_")
27 | .Build();
28 | }
29 |
30 | public void ConfigureServices(IServiceCollection services)
31 | {
32 | #region Echoer Bot
33 |
34 | var echoBotOptions = new BotOptions();
35 | Configuration.GetSection("EchoerBot").Bind(echoBotOptions);
36 |
37 | services.AddTelegramBot(echoBotOptions)
38 | .AddUpdateHandler()
39 | .Configure();
40 | services.AddTask>();
41 |
42 | #endregion
43 |
44 | #region Greeter Bot
45 |
46 | services.AddTelegramBot(Configuration.GetSection("GreeterBot"))
47 | .AddUpdateHandler()
48 | .AddUpdateHandler()
49 | .AddUpdateHandler()
50 | .Configure();
51 | services.AddTask>();
52 |
53 | #endregion
54 | }
55 |
56 | public void Configure(IApplicationBuilder app, IHostingEnvironment env,
57 | ILoggerFactory loggerFactory)
58 | {
59 | loggerFactory.AddConsole(Configuration.GetSection("Logging"));
60 | loggerFactory.AddDebug();
61 |
62 | ILogger logger = loggerFactory.CreateLogger();
63 |
64 | if (env.IsDevelopment())
65 | {
66 | app.UseDeveloperExceptionPage();
67 | }
68 | else
69 | {
70 | app.UseExceptionHandler(appBuilder =>
71 | appBuilder.Run(context =>
72 | {
73 | context.Response.StatusCode = StatusCodes.Status500InternalServerError;
74 | return Task.CompletedTask;
75 | })
76 | );
77 | }
78 |
79 | #region Echoer Bot
80 |
81 | if (env.IsDevelopment())
82 | {
83 | app.UseTelegramBotLongPolling();
84 | app.StartTask>(TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(3));
85 | logger.LogInformation("Update getting task is scheduled for bot " + nameof(EchoerBot));
86 | }
87 | else
88 | {
89 | app.UseTelegramBotWebhook();
90 | logger.LogInformation("Webhook is set for bot " + nameof(EchoerBot));
91 | }
92 |
93 | #endregion
94 |
95 | #region Greeter Bot
96 |
97 | if (env.IsDevelopment())
98 | {
99 | app.UseTelegramBotLongPolling();
100 | app.StartTask>(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(3));
101 | logger.LogInformation("Update getting task is scheduled for bot " + nameof(GreeterBot));
102 | }
103 | else
104 | {
105 | app.UseTelegramBotWebhook();
106 | logger.LogInformation("Webhook is set for bot " + nameof(GreeterBot));
107 | }
108 |
109 | #endregion
110 |
111 | app.Run(async context => { await context.Response.WriteAsync("Hello World!"); });
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/sample/SampleBots/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "EchoerBot": {
3 | "ApiToken": "",
4 | "BotUserName": "sample_echoer_bot",
5 | "PathToCertificate": "",
6 | "BaseUrl": "https://example.com/bots/"
7 | },
8 | "GreeterBot": {
9 | "ApiToken": "",
10 | "BotUserName": "sample_greeter_bot",
11 | "PathToCertificate": "",
12 | "BaseUrl": "https://example.com/bots/"
13 | },
14 | "Logging": {
15 | "IncludeScopes": false,
16 | "LogLevel": {
17 | "Default": "Debug",
18 | "Microsoft": "Information",
19 | "System": "Information"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sample/SampleEchoBot/Data/IRepository.cs:
--------------------------------------------------------------------------------
1 | namespace SampleEchoBot.Data
2 | {
3 | public interface IRepository
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/sample/SampleEchoBot/EchoBot.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.Extensions.Logging;
5 | using Microsoft.Extensions.Options;
6 | using Telegram.Bot.Framework;
7 | using Telegram.Bot.Types;
8 | using Telegram.Bot.Types.Enums;
9 |
10 | namespace SampleEchoBot
11 | {
12 | public class EchoBot : BotBase
13 | {
14 | private readonly ILogger _logger;
15 |
16 | public EchoBot(IOptions> botOptions, ILogger logger)
17 | : base(botOptions)
18 | {
19 | _logger = logger;
20 | }
21 |
22 | public override async Task HandleUnknownUpdate(Update update)
23 | {
24 | _logger.LogWarning("Unable to handle update of type `{0}`", update.Type);
25 |
26 | string text;
27 | int replyToMesageId = default(int);
28 |
29 | switch (update.Type)
30 | {
31 | case UpdateType.Message when
32 | new[] {ChatType.Private, ChatType.Group, ChatType.Supergroup}.Contains(update.Message.Chat.Type):
33 | text = $"Unable to handle message update of type `{update.Message.Type}`.";
34 | replyToMesageId = update.Message.MessageId;
35 | break;
36 | default:
37 | text = null;
38 | break;
39 | }
40 |
41 | if (text != null)
42 | {
43 | await Client.SendTextMessageAsync(update.Message.Chat.Id, text, ParseMode.Markdown,
44 | replyToMessageId: replyToMesageId);
45 | }
46 | }
47 |
48 | public override Task HandleFaultedUpdate(Update update, Exception e)
49 | {
50 | _logger.LogError($"Exception occured in handling update of type `{0}`: {1}", update.Type, e.Message);
51 |
52 | return Task.CompletedTask;
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/sample/SampleEchoBot/EchoCommand.cs:
--------------------------------------------------------------------------------
1 | //using System.Threading.Tasks;
2 | //using Telegram.Bot.Abstractions;
3 | //using Telegram.Bot.Framework;
4 | //using Telegram.Bot.Types;
5 | //
6 | //namespace SampleEchoBot
7 | //{
8 | // public class EchoCommand : CommandBase
9 | // {
10 | // public EchoCommand() : base(name: "echo")
11 | // {
12 | // }
13 | //
14 | // public override async Task HandleCommandAsync(Update update, CommandArgs args)
15 | // {
16 | // string replyText = string.IsNullOrWhiteSpace(args.ArgsInput) ? "Echo What?" : args.ArgsInput;
17 | //
18 | // await Bot.Client.SendTextMessageAsync(
19 | // update.Message.Chat.Id,
20 | // replyText,
21 | // replyToMessageId: update.Message.MessageId);
22 | // }
23 | // }
24 | //
25 | // public class CommandArgs : ICommandArgs
26 | // {
27 | // public string RawInput { get; set; }
28 | // public string ArgsInput { get; set; }
29 | // }
30 | //}
--------------------------------------------------------------------------------
/sample/SampleEchoBot/Program.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace SampleEchoBot
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | var host = new WebHostBuilder()
11 | .UseKestrel()
12 | .UseContentRoot(Directory.GetCurrentDirectory())
13 | .UseIISIntegration()
14 | .UseStartup()
15 | .UseApplicationInsights()
16 | .Build();
17 |
18 | host.Run();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sample/SampleEchoBot/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:58090/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "environmentVariables": {
14 | "ASPNETCORE_ENVIRONMENT": "Development"
15 | }
16 | },
17 | "SampleEchoBot": {
18 | "commandName": "Project",
19 | "environmentVariables": {
20 | "ASPNETCORE_ENVIRONMENT": "Development"
21 | },
22 | "applicationUrl": "http://localhost:58091"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/sample/SampleEchoBot/SampleEchoBot.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp1.1
4 | latest
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sample/SampleEchoBot/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Microsoft.Extensions.Logging;
10 | using Telegram.Bot.Abstractions;
11 | using Telegram.Bot.Framework;
12 |
13 | namespace SampleEchoBot
14 | {
15 | public class Startup
16 | {
17 | private readonly IConfigurationRoot _configuration;
18 |
19 | public Startup(IHostingEnvironment env)
20 | {
21 | var builder = new ConfigurationBuilder()
22 | .SetBasePath(env.ContentRootPath)
23 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
24 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
25 | .AddEnvironmentVariables();
26 | _configuration = builder.Build();
27 | }
28 |
29 | public void ConfigureServices(IServiceCollection services)
30 | {
31 | // services.AddTelegramBot(_configuration.GetSection("EchoBot"))
32 | //// .AddUpdateHandler()
33 | // .AddUpdateHandler<>()
34 | // .Configure();
35 | }
36 |
37 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
38 | {
39 | loggerFactory.AddConsole(_configuration.GetSection("Logging"));
40 | loggerFactory.AddDebug();
41 | ILogger logger = loggerFactory.CreateLogger();
42 |
43 | if (env.IsDevelopment())
44 | {
45 | app.UseDeveloperExceptionPage();
46 |
47 | var source = new CancellationTokenSource();
48 | Task.Factory.StartNew(() =>
49 | {
50 | logger.LogDebug("Press Enter to stop bot manager...");
51 | Console.ReadLine();
52 | source.Cancel();
53 | });
54 |
55 | Task.Factory.StartNew(async () =>
56 | {
57 | var botManager = app.ApplicationServices.GetRequiredService>();
58 |
59 | // make sure webhook is disabled so we can use long-polling
60 | await botManager.SetWebhookStateAsync(false);
61 | logger.LogDebug("Webhook is disabled. Staring update handling...");
62 |
63 | while (!source.IsCancellationRequested)
64 | {
65 | await Task.Delay(3_000);
66 | await botManager.GetAndHandleNewUpdatesAsync();
67 | }
68 | logger.LogDebug("Bot manager stopped.");
69 | }).ContinueWith(t =>
70 | {
71 | if (t.IsFaulted) throw t.Exception;
72 | });
73 | }
74 | else
75 | {
76 | app.UseExceptionHandler(appBuilder =>
77 | appBuilder.Run(context =>
78 | {
79 | context.Response.StatusCode = StatusCodes.Status500InternalServerError;
80 | return Task.CompletedTask;
81 | })
82 | );
83 |
84 | logger.LogInformation($"Setting webhook for {nameof(EchoBot)}...");
85 | // app.UseTelegramBotWebhook();
86 | logger.LogInformation("Webhook is set for bot " + nameof(EchoBot));
87 | }
88 |
89 |
90 | app.Run(async context =>
91 | {
92 | await context.Response.WriteAsync("Hello World!");
93 | });
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/sample/SampleEchoBot/TextMessageHandler.cs:
--------------------------------------------------------------------------------
1 | //using System.Threading.Tasks;
2 | //using Telegram.Bot;
3 | //using Telegram.Bot.Abstractions;
4 | //using Telegram.Bot.Types;
5 | //
6 | //namespace SampleEchoBot
7 | //{
8 | // public class TextMessageHandler : IUpdateHandler
9 | // {
10 | // private ITelegramBotClient BotClient => _bot.Client;
11 | // private readonly IBot _bot;
12 | //
13 | // public TextMessageHandler(IBot bot)
14 | // {
15 | // _bot = bot;
16 | // }
17 | //
18 | // public bool CanHandle(IUpdateContext context) =>
19 | // context.Update.Message?.Text != null;
20 | //
21 | // public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
22 | // {
23 | // Message msg = context.Update.Message;
24 | //
25 | // await BotClient.SendTextMessageAsync(
26 | // msg.Chat, "You said:\n" + msg.Text
27 | // );
28 | //
29 | // await next(context);
30 | // }
31 | // }
32 | //}
--------------------------------------------------------------------------------
/sample/SampleEchoBot/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Debug",
6 | "Microsoft": "Information",
7 | "System": "Information"
8 | }
9 | },
10 | "EchoBot": {
11 | "ApiToken": "{your-bots-api-token}",
12 | "BotUserName": "{your-bots-username}",
13 | "PathToCertificate": "",
14 | "WebhookUrl": "https://example.com/bots/{bot}/webhook/{token}"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/sample/SampleGames/BotUpdateGetterTask.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Microsoft.Extensions.Logging;
4 | using RecurrentTasks;
5 | using Telegram.Bot.Framework.Abstractions;
6 |
7 | namespace SampleGames
8 | {
9 | public class BotUpdateGetterTask : IRunnable
10 | where TBot : class, IBot
11 | {
12 | private readonly IBotManager _botManager;
13 |
14 | private readonly ILogger _logger;
15 |
16 | public BotUpdateGetterTask(IBotManager botManager,
17 | ILogger> logger)
18 | {
19 | _botManager = botManager;
20 | _logger = logger;
21 | }
22 |
23 | public void Run(ITask currentTask, CancellationToken cancellationToken)
24 | {
25 | Task.Factory.StartNew(async () =>
26 | {
27 | _logger.LogTrace($"{typeof(TBot).Name}: Checking for updates...");
28 | await _botManager.GetAndHandleNewUpdatesAsync();
29 | _logger.LogTrace($"{typeof(TBot).Name}: Handling updates finished");
30 | }, cancellationToken).ContinueWith(task =>
31 | {
32 | if (task.IsFaulted)
33 | {
34 | _logger.LogError("{0}", task.Exception.Message);
35 | throw task.Exception;
36 | }
37 | }, cancellationToken);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/sample/SampleGames/Bots/CrazyCircle/CrazyCircleBot.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.Extensions.Logging;
4 | using Microsoft.Extensions.Options;
5 | using Telegram.Bot.Framework;
6 | using Telegram.Bot.Types;
7 |
8 | namespace SampleGames.Bots.CrazyCircle
9 | {
10 | public class CrazyCircleBot : BotBase
11 | {
12 | private readonly ILogger _logger;
13 |
14 | public CrazyCircleBot(IOptions> botOptions,
15 | ILogger logger)
16 | : base(botOptions)
17 | {
18 | _logger = logger;
19 | }
20 |
21 | public override Task HandleUnknownUpdate(Update update)
22 | {
23 | _logger.LogWarning("Don't know how to handle update of type `{0}`", update.Type);
24 | return Task.CompletedTask;
25 | }
26 |
27 | public override Task HandleFaultedUpdate(Update update, Exception e)
28 | {
29 | _logger.LogError("Error in handling update of type `{0}`.{1}{2}",
30 | update.Type, Environment.NewLine, e);
31 | return Task.CompletedTask;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/sample/SampleGames/Bots/CrazyCircle/CrazyCircleGameHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.DataProtection;
2 | using Microsoft.Extensions.Logging;
3 | using Telegram.Bot.Framework;
4 |
5 | namespace SampleGames.Bots.CrazyCircle
6 | {
7 | public class CrazyCircleGameHandler : GameHandlerBase
8 | {
9 | public CrazyCircleGameHandler(IDataProtectionProvider protectionProvider, ILogger logger)
10 | : base(protectionProvider, Constants.GameShortName, logger)
11 | {
12 |
13 | }
14 |
15 | private static class Constants
16 | {
17 | public const string GameShortName = "crazycircle";
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/sample/SampleGames/Bots/CrazyCircle/StartCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Telegram.Bot.Framework;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 |
6 | namespace SampleGames.Bots.CrazyCircle
7 | {
8 | public class StartCommandArgs : ICommandArgs
9 | {
10 | public string RawInput { get; set; }
11 |
12 | public string ArgsInput { get; set; }
13 | }
14 |
15 | public class StartCommand : CommandBase
16 | {
17 | public StartCommand()
18 | : base(Constants.CommandName)
19 | {
20 |
21 | }
22 |
23 | public override async Task HandleCommand(Update update, StartCommandArgs args)
24 | {
25 | await Bot.Client.SendGameAsync(update.Message.Chat.Id, "crazycircle");
26 |
27 | return UpdateHandlingResult.Handled;
28 | }
29 |
30 | private static class Constants
31 | {
32 | public const string CommandName = "start";
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sample/SampleGames/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.Configuration;
5 |
6 | namespace SampleGames
7 | {
8 | public class Program
9 | {
10 | public static void Main(string[] args)
11 | {
12 | Console.Title = "Telegram.Bot.Framework - Sample Games";
13 |
14 | var config = new ConfigurationBuilder()
15 | .AddCommandLine(args)
16 | .Build();
17 |
18 | var host = new WebHostBuilder()
19 | .UseKestrel()
20 | .UseContentRoot(Directory.GetCurrentDirectory())
21 | .UseIISIntegration()
22 | .UseConfiguration(config)
23 | .UseStartup()
24 | .UseApplicationInsights()
25 | .Build();
26 |
27 | host.Run();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/SampleGames/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "anonymousAuthentication": true,
4 | "iisExpress": {
5 | "applicationUrl": "http://localhost:60058/",
6 | "sslPort": 0
7 | },
8 | "windowsAuthentication": false
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "environmentVariables": {
14 | "ASPNETCORE_ENVIRONMENT": "Development"
15 | }
16 | },
17 | "SampleGame": {
18 | "applicationUrl": "http://localhost:60059",
19 | "commandName": "Project",
20 | "environmentVariables": {
21 | "ASPNETCORE_ENVIRONMENT": "Development"
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/sample/SampleGames/SampleGames.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp1.1
4 |
5 |
6 | $(PackageTargetFallback);portable-net45+win8+wp8+wpa81;
7 | SampleGames
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/SampleGames/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.Extensions.Logging;
9 | using RecurrentTasks;
10 | using SampleGames.Bots.CrazyCircle;
11 | using Telegram.Bot.Framework;
12 |
13 | namespace SampleGames
14 | {
15 | public class Startup
16 | {
17 | private readonly IConfigurationRoot _configuration;
18 |
19 | public Startup(IHostingEnvironment env)
20 | {
21 | var builder = new ConfigurationBuilder()
22 | .SetBasePath(env.ContentRootPath)
23 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
24 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
25 | .AddEnvironmentVariables();
26 | _configuration = builder.Build();
27 | }
28 |
29 | public void ConfigureServices(IServiceCollection services)
30 | {
31 | services.AddCors();
32 | services.AddDataProtection();
33 |
34 | #region CrazyCircle Bot
35 |
36 | services.AddTelegramBot(_configuration.GetSection("CrazyCircleBot"))
37 | .AddUpdateHandler()
38 | .AddUpdateHandler()
39 | .Configure();
40 | services.AddTask>();
41 |
42 | #endregion
43 | }
44 |
45 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
46 | {
47 | loggerFactory.AddConsole(_configuration.GetSection("Logging"));
48 | loggerFactory.AddDebug();
49 |
50 | ILogger logger = loggerFactory.CreateLogger();
51 |
52 | if (env.IsDevelopment())
53 | {
54 | app.UseDeveloperExceptionPage();
55 | app.UseBrowserLink();
56 | }
57 | else
58 | {
59 | app.UseExceptionHandler(appBuilder =>
60 | appBuilder.Run(context =>
61 | {
62 | context.Response.StatusCode = StatusCodes.Status500InternalServerError;
63 | return Task.CompletedTask;
64 | })
65 | );
66 | }
67 |
68 | app.UseCors(builder => builder
69 | .WithOrigins("http://crazy-circle-game.apphb.com")
70 | .WithMethods(HttpMethods.Get, HttpMethods.Post)
71 | .DisallowCredentials()
72 | );
73 |
74 | app.UseStaticFiles();
75 |
76 | #region CrazyCircle Bot
77 |
78 | app.UseTelegramGame();
79 |
80 | if (env.IsDevelopment())
81 | {
82 | app.UseTelegramBotLongPolling();
83 | app.StartTask>(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(3));
84 | logger.LogInformation("Update getting task is scheduled for bot " + nameof(CrazyCircleBot));
85 | }
86 | else
87 | {
88 | app.UseTelegramBotWebhook();
89 | logger.LogInformation("Webhook is set for bot " + nameof(CrazyCircleBot));
90 | }
91 |
92 | #endregion
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/sample/SampleGames/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Warning"
6 | }
7 | },
8 | "CrazyCircleBot": {
9 | "ApiToken": "",
10 | "BotUserName": "CrazyCircleBot",
11 | "PathToCertificate": "",
12 | "WebhookUrl": "https://example.com/bots/{bot}/webhook/{token}",
13 | "GameOptions": [
14 | {
15 | "ShortName": "crazycircle",
16 | "Url": "http://crazy-circle-game.apphb.com/index.html",
17 | "ScoresUrl": "http://example.com/bots/{bot}/games/{game}/scores"
18 | }
19 | ]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sample/SampleGames/wwwroot/CrazyCircleBot/Games/CrazyCircle/assets/scripts/TgBF.js:
--------------------------------------------------------------------------------
1 | function getValueFromUrl(url, key) {
2 | var value = null;
3 | var tokens = url.substr(url.indexOf('#') + 1).split('&');
4 | for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
5 | var t = tokens_1[_i];
6 | if (t.match("^" + key + "=.+$")) {
7 | value = t.substr(key.length + 1);
8 | value = decodeURIComponent(value);
9 | }
10 | }
11 | return value;
12 | }
13 | function getPlayerId() {
14 | return getValueFromUrl(location.href, "id");
15 | }
16 | function getScoreUrl() {
17 | return getValueFromUrl(location.href, "gameScoreUrl");
18 | }
19 | function botGameScoreUrlExists() {
20 | var scoreUrl = getScoreUrl();
21 | return scoreUrl !== null && scoreUrl.toString().length > 1;
22 | }
23 | //# sourceMappingURL=TgBF.js.map
--------------------------------------------------------------------------------
/sample/SampleGames/wwwroot/CrazyCircleBot/Games/CrazyCircle/assets/scripts/script.js:
--------------------------------------------------------------------------------
1 | var Script;
2 | (function (Script) {
3 | var Stage = createjs.Stage;
4 | var Shape = createjs.Shape;
5 | var Ticker = createjs.Ticker;
6 | var Tween = createjs.Tween;
7 | var Ease = createjs.Ease;
8 | var Touch = createjs.Touch;
9 | var Text = createjs.Text;
10 | var isMobile = Touch.isSupported() === true;
11 | var canvas;
12 | var stage;
13 | var bg;
14 | var circle;
15 | var scoreText;
16 | var score = 0;
17 | var counter = 7;
18 | var counterInterval;
19 | var counterText;
20 | var hexValues = "0123456789ABCDEF";
21 | function generateColor() {
22 | var color = "#";
23 | for (var i = 0; i < 6; i++) {
24 | var i_1 = (Math.random() * 832740) % 16;
25 | color += hexValues.charAt(i_1);
26 | }
27 | return color;
28 | }
29 | function showScores() {
30 | if (!(this.readyState === 4 && this.status === 200)) {
31 | return;
32 | }
33 | var highScoreTexts = [];
34 | var highScores = JSON.parse(this.response);
35 | for (var i = 0; i < highScores.length; i++) {
36 | var score_1 = highScores[i];
37 | var text = new Text(score_1.score + " " + score_1.user.first_name, "12pt sans", "yellow");
38 | text.x = (canvas.width / 2) - (text.getMeasuredWidth() / 2);
39 | text.y = 5 + (1.5 * text.getMeasuredLineHeight() * i);
40 | highScoreTexts[i] = text;
41 | stage.addChild(text);
42 | }
43 | }
44 | function sendScore() {
45 | var scoresUrl = getScoreUrl();
46 | var playerid = getPlayerId();
47 | var xhr = new XMLHttpRequest();
48 | var data = {
49 | playerId: playerid,
50 | score: score
51 | };
52 | xhr.open("POST", scoresUrl);
53 | xhr.send(JSON.stringify(data));
54 | xhr.addEventListener("readystatechange", function () {
55 | if (xhr.readyState === 4) {
56 | getScores();
57 | }
58 | });
59 | }
60 | function getScores() {
61 | var scoresUrl = getScoreUrl();
62 | var playerid = getPlayerId();
63 | var xhr = new XMLHttpRequest();
64 | xhr.addEventListener("load", showScores);
65 | xhr.open("GET", scoresUrl + ("?id=" + encodeURIComponent(playerid)));
66 | xhr.send();
67 | }
68 | function handleCounterInterval() {
69 | if (counter < 2) {
70 | clearInterval(counterInterval);
71 | stage.removeChild(circle, counterText);
72 | circle.removeEventListener("pressmove", beginMoveCircle);
73 | counter = null;
74 | counterInterval = null;
75 | if (botGameScoreUrlExists()) {
76 | sendScore();
77 | }
78 | }
79 | else {
80 | counter--;
81 | counterText.text = counter.toString();
82 | }
83 | }
84 | function beginMoveCircle(ev) {
85 | function setBgToDark() {
86 | bg.graphics
87 | .clear()
88 | .beginFill("#555")
89 | .drawRect(0, 0, canvas.width, canvas.height);
90 | }
91 | var distance = Math.sqrt(Math.pow(circle.x - ev.stageX, 2) +
92 | Math.pow(circle.y - ev.stageY, 2));
93 | distance = Math.floor(distance * Math.random() / 10);
94 | score += distance;
95 | scoreText.text = score.toString();
96 | var bgColor = "#458";
97 | var distanceThreshold = 12;
98 | if (isMobile) {
99 | distanceThreshold = 3;
100 | }
101 | if (distance > distanceThreshold) {
102 | bgColor = generateColor();
103 | }
104 | bg.graphics
105 | .clear()
106 | .beginFill(bgColor)
107 | .drawRect(0, 0, canvas.width, canvas.height);
108 | Tween.get(circle)
109 | .to({
110 | x: ev.stageX,
111 | y: ev.stageY
112 | }, undefined, Ease.backInOut)
113 | .call(setBgToDark);
114 | }
115 | function draw() {
116 | bg = new Shape();
117 | bg.graphics
118 | .beginFill('#555')
119 | .drawRect(0, 0, canvas.width, canvas.height);
120 | stage.addChild(bg);
121 | circle = new Shape();
122 | circle.graphics
123 | .beginFill("red")
124 | .beginStroke('yellow')
125 | .drawCircle(0, 0, 20);
126 | circle.x = canvas.width / 2;
127 | circle.y = canvas.height / 2;
128 | stage.addChild(circle);
129 | circle.addEventListener("pressmove", beginMoveCircle);
130 | scoreText = new Text(score.toString(), "14pt sans", "yellow");
131 | scoreText.x = 4;
132 | scoreText.y = 7;
133 | stage.addChild(scoreText);
134 | counterText = new Text(counter.toString(), "14pt sans", "#fff");
135 | counterText.x = canvas.width - counterText.getMeasuredWidth() - 10;
136 | counterText.y = 7;
137 | stage.addChild(counterText);
138 | }
139 | function resizeCanvas() {
140 | canvas.width = window.innerWidth;
141 | canvas.height = window.innerHeight;
142 | }
143 | function init() {
144 | canvas = document.getElementById("canvas");
145 | window.addEventListener("resize", resizeCanvas, false);
146 | resizeCanvas();
147 | stage = new Stage(canvas);
148 | Touch.enable(stage);
149 | Ticker.framerate = 60;
150 | Ticker.addEventListener("tick", function () { return stage.update(); });
151 | draw();
152 | counterInterval = setInterval(handleCounterInterval, 1 * 1000);
153 | }
154 | Script.init = init;
155 | })(Script || (Script = {}));
156 | window.addEventListener("load", Script.init);
157 | //# sourceMappingURL=script.js.map
--------------------------------------------------------------------------------
/sample/SampleGames/wwwroot/CrazyCircleBot/Games/CrazyCircle/assets/styles/game.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | html, body {
7 | width: 100%;
8 | height: 100%;
9 | }
10 |
11 | canvas {
12 | display: block;
13 | background-color: #555;
14 | }
15 |
--------------------------------------------------------------------------------
/sample/SampleGames/wwwroot/CrazyCircleBot/Games/CrazyCircle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/SampleGames/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TelegramBots/Telegram.Bot.Framework/6ac27f80d1ae29f86ccf790d7d2a5b21759182ec/sample/SampleGames/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/scripts/build/index.js:
--------------------------------------------------------------------------------
1 | const $ = require('shelljs')
2 | const path = require('path')
3 | require('../logging')
4 |
5 | $.config.fatal = true
6 | const root = path.resolve(`${__dirname}/../..`)
7 |
8 |
9 | try {
10 | console.info(`building Docker image`)
11 |
12 | console.debug('building the solution with "Release" configuration and "quickstart:latest" tag')
13 | $.cd(root)
14 | $.exec(
15 | `docker build --tag "quickstart:latest" --no-cache .`
16 | )
17 | } catch (e) {
18 | console.error(e)
19 | process.exit(1)
20 | }
21 |
22 | console.info(`✅ Build succeeded`)
23 |
--------------------------------------------------------------------------------
/scripts/deploy/deploy_docker_registry.js:
--------------------------------------------------------------------------------
1 | const $ = require('shelljs')
2 | require('../logging')
3 |
4 | $.config.fatal = true
5 |
6 |
7 | exports.deploy = function (source, target, user, pass) {
8 | console.info(`pushing docker local image ${source} to ${target}`)
9 |
10 | $.exec(`docker tag ${source} ${target}`)
11 | $.exec(`docker login --username ${user} --password ${pass}`)
12 | $.exec(`docker push ${target}`)
13 | $.exec('docker logout')
14 | }
15 |
--------------------------------------------------------------------------------
/scripts/deploy/deploy_heroku.js:
--------------------------------------------------------------------------------
1 | const $ = require('shelljs')
2 | require('../logging')
3 |
4 | $.config.fatal = true
5 |
6 |
7 | function validate_args(...args) {
8 | if (!args.every(x => x && x.length)) {
9 | throw `All the required parameters should have value.`
10 | }
11 | }
12 |
13 | function push_image_to_heroku(app, source, dyno, user, token) {
14 | console.info('pushing docker image to heroku')
15 |
16 | console.debug('connecting to heroku docker registry')
17 | $.exec(`docker login --username ${user} --password ${token} registry.heroku.com`)
18 |
19 | console.debug('tagging the image')
20 | $.exec(`docker tag ${source} registry.heroku.com/${app}/${dyno}`)
21 |
22 | console.debug('pushing the image')
23 | $.exec(`docker push registry.heroku.com/${app}/${dyno}`)
24 | }
25 |
26 | function release_heroku_app(app, source, dyno, token) {
27 | console.info('deploying the image to heroku dyno')
28 |
29 | console.debug(`getting docker image ID`)
30 | const image_id = $.exec(`docker inspect ${source} --format={{.Id}}`).stdout.trim()
31 |
32 | console.debug(`upgrading to new release`)
33 | const post_data = JSON.stringify({
34 | updates: [{
35 | type: dyno,
36 | docker_image: image_id
37 | }]
38 | })
39 |
40 | $.exec(
41 | `curl -X PATCH https://api.heroku.com/apps/${app}/formation ` +
42 | `-H 'Authorization: Bearer ${token}' ` +
43 | `-H "Content-Type: application/json" ` +
44 | `-H "Accept: application/vnd.heroku+json; version=3.docker-releases" ` +
45 | `-d ${JSON.stringify(post_data)}`
46 | )
47 | }
48 |
49 |
50 | exports.deploy = function (app, source, dyno, user, token) {
51 | validate_args(app, source, dyno, user, token)
52 | push_image_to_heroku(app, source, dyno, user, token)
53 | release_heroku_app(app, source, dyno, token)
54 | }
--------------------------------------------------------------------------------
/scripts/deploy/deploy_settings.js:
--------------------------------------------------------------------------------
1 | exports.get_deployment_settings = () => {
2 | const jsonValue = process.env['DEPLOY_SETTINGS_JSON']
3 | let settings;
4 | try {
5 | settings = JSON.parse(jsonValue)
6 | } catch (e) {
7 | throw `Value of "DEPLOY_SETTINGS_JSON" environment variable is not valid JSON.`
8 | }
9 |
10 | return settings
11 | }
12 |
--------------------------------------------------------------------------------
/scripts/deploy/index.js:
--------------------------------------------------------------------------------
1 | require('../logging')
2 |
3 |
4 | function get_environment_name() {
5 | console.info('verifying environment name')
6 |
7 | const environment_name = process.argv[process.argv.length - 1]
8 | if (environment_name && environment_name.length) {
9 | console.debug(`environment is ${environment_name}.`)
10 | return environment_name
11 | } else {
12 | throw `No environment name is passed.\n` +
13 | `\tExample: node ci/deploy Staging`
14 | }
15 | }
16 |
17 | function get_deployments_for_env(environment_name) {
18 | console.info(`finding deployments for environment ${environment_name}.`)
19 |
20 | const jsonValue = process.env['DEPLOY_SETTINGS_JSON']
21 | let deployment_map;
22 | try {
23 | deployment_map = JSON.parse(jsonValue)
24 | } catch (e) {
25 | throw `Value of "DEPLOY_SETTINGS_JSON" environment variable is not valid JSON.`
26 | }
27 |
28 | const env_deployments = deployment_map[environment_name];
29 | if (!env_deployments) {
30 | throw `There are no field for environment ${environment_name} in "DEPLOY_SETTINGS_JSON" value.`
31 | }
32 | if (!(Array.isArray(env_deployments) && env_deployments.length)) {
33 | console.warn(`There are deployments specified for environment ${environment_name}.`)
34 | }
35 |
36 | console.debug(`${env_deployments.length || 0} deployments found.`)
37 |
38 | return env_deployments
39 | }
40 |
41 | function deploy(environment_name, deployment) {
42 | console.info(`deploying to ${deployment.type} for environment ${environment_name}.`)
43 | const docker = require('./deploy_docker_registry')
44 | const heorku = require('./deploy_heroku')
45 |
46 | if (deployment.type === 'docker') {
47 | docker.deploy(
48 | deployment.options.source,
49 | deployment.options.target,
50 | deployment.options.user,
51 | deployment.options.pass
52 | )
53 | } else if (deployment.type === 'heroku') {
54 | heorku.deploy(
55 | deployment.options.app,
56 | deployment.options.source,
57 | deployment.options.dyno,
58 | deployment.options.user,
59 | deployment.options.token
60 | )
61 | } else {
62 | throw `Invalid deployment type ${deployment.type}.`
63 | }
64 | }
65 |
66 |
67 | try {
68 | const environment_name = get_environment_name()
69 | get_deployments_for_env(environment_name)
70 | .forEach(d => deploy(environment_name, d))
71 | } catch (e) {
72 | console.error(e)
73 | process.exit(1)
74 | }
75 |
--------------------------------------------------------------------------------
/scripts/logging.js:
--------------------------------------------------------------------------------
1 | const chalk = require('chalk');
2 | const $ = require('shelljs')
3 |
4 | if (process.env['TRAVIS'] && process.env['CI']) {
5 | console.info = m => $.echo("\033[1;34m", m, "\033[0m")
6 | console.debug = m => $.echo("\033[0;32m", m, "\033[0m")
7 | console.warn = m => $.echo("\033[1;33m", m, "\033[0m")
8 | console.error = m => $.echo("\033[1;31m", m, "\033[0m")
9 | } else {
10 | console.info = m => console.log(chalk.blue.bold(m))
11 | console.debug = m => console.log(chalk.green.bold(m))
12 | console.warn = m => console.log(chalk.yellow.bold(m))
13 | console.error = m => console.log(chalk.red.bold(m))
14 | }
--------------------------------------------------------------------------------
/scripts/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scripts",
3 | "version": "0.1.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ansi-styles": {
8 | "version": "3.2.1",
9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
11 | "requires": {
12 | "color-convert": "1.9.3"
13 | }
14 | },
15 | "balanced-match": {
16 | "version": "1.0.0",
17 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
18 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
19 | },
20 | "brace-expansion": {
21 | "version": "1.1.11",
22 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
23 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
24 | "requires": {
25 | "balanced-match": "1.0.0",
26 | "concat-map": "0.0.1"
27 | }
28 | },
29 | "chalk": {
30 | "version": "2.4.1",
31 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
32 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
33 | "requires": {
34 | "ansi-styles": "3.2.1",
35 | "escape-string-regexp": "1.0.5",
36 | "supports-color": "5.5.0"
37 | }
38 | },
39 | "color-convert": {
40 | "version": "1.9.3",
41 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
42 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
43 | "requires": {
44 | "color-name": "1.1.3"
45 | }
46 | },
47 | "color-name": {
48 | "version": "1.1.3",
49 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
50 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
51 | },
52 | "concat-map": {
53 | "version": "0.0.1",
54 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
55 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
56 | },
57 | "escape-string-regexp": {
58 | "version": "1.0.5",
59 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
60 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
61 | },
62 | "fs.realpath": {
63 | "version": "1.0.0",
64 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
65 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
66 | },
67 | "glob": {
68 | "version": "7.1.3",
69 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
70 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
71 | "requires": {
72 | "fs.realpath": "1.0.0",
73 | "inflight": "1.0.6",
74 | "inherits": "2.0.3",
75 | "minimatch": "3.0.4",
76 | "once": "1.4.0",
77 | "path-is-absolute": "1.0.1"
78 | }
79 | },
80 | "has-flag": {
81 | "version": "3.0.0",
82 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
83 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
84 | },
85 | "inflight": {
86 | "version": "1.0.6",
87 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
88 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
89 | "requires": {
90 | "once": "1.4.0",
91 | "wrappy": "1.0.2"
92 | }
93 | },
94 | "inherits": {
95 | "version": "2.0.3",
96 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
97 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
98 | },
99 | "interpret": {
100 | "version": "1.1.0",
101 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
102 | "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
103 | },
104 | "minimatch": {
105 | "version": "3.0.4",
106 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
107 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
108 | "requires": {
109 | "brace-expansion": "1.1.11"
110 | }
111 | },
112 | "once": {
113 | "version": "1.4.0",
114 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
115 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
116 | "requires": {
117 | "wrappy": "1.0.2"
118 | }
119 | },
120 | "path-is-absolute": {
121 | "version": "1.0.1",
122 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
123 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
124 | },
125 | "path-parse": {
126 | "version": "1.0.6",
127 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
128 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
129 | },
130 | "rechoir": {
131 | "version": "0.6.2",
132 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
133 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
134 | "requires": {
135 | "resolve": "1.8.1"
136 | }
137 | },
138 | "resolve": {
139 | "version": "1.8.1",
140 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
141 | "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
142 | "requires": {
143 | "path-parse": "1.0.6"
144 | }
145 | },
146 | "shelljs": {
147 | "version": "0.8.2",
148 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.2.tgz",
149 | "integrity": "sha512-pRXeNrCA2Wd9itwhvLp5LZQvPJ0wU6bcjaTMywHHGX5XWhVN2nzSu7WV0q+oUY7mGK3mgSkDDzP3MgjqdyIgbQ==",
150 | "requires": {
151 | "glob": "7.1.3",
152 | "interpret": "1.1.0",
153 | "rechoir": "0.6.2"
154 | }
155 | },
156 | "supports-color": {
157 | "version": "5.5.0",
158 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
159 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
160 | "requires": {
161 | "has-flag": "3.0.0"
162 | }
163 | },
164 | "wrappy": {
165 | "version": "1.0.2",
166 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
167 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/scripts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scripts",
3 | "version": "0.1.0",
4 | "description": "",
5 | "scripts": {
6 | "start": "npm run build && npm run test",
7 | "build": "node build",
8 | "test": "node test"
9 | },
10 | "license": "ISC",
11 | "dependencies": {
12 | "chalk": "^2.3.1",
13 | "shelljs": "^0.8.1"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/scripts/publish-bots.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | current_dir="`pwd`"
5 | root_dir="${current_dir}/.."
6 |
7 | declare -a sample_projects=("SampleEchoBot" "SampleGames")
8 |
9 | for project in "${sample_projects[@]}"
10 | do
11 | echo; echo "@> Build and publish project ${project}"; echo;
12 |
13 | cd "${root_dir}/sample/${project}" &&
14 | rm -rf bin/publish/ &&
15 | dotnet publish -c Release -o bin/publish/ &&
16 | cp -v "${current_dir}/Dockerfile" Dockerfile
17 | done
18 |
19 | echo; echo "@> Copy nginx Dockerfile to its context"; echo;
20 |
21 | cd "${current_dir}" &&
22 | cp -v nginx.Dockerfile nginx/Dockerfile
23 |
24 | echo; echo "@> Build docker compose"; echo;
25 |
26 | cd "${current_dir}" &&
27 | docker-compose build
28 |
29 | echo; echo "@> Remove copied Dockerfiles"; echo;
30 |
31 | find "${root_dir}/sample" -type f -name Dockerfile -print0 | xargs -0 rm -v
32 | rm -v "${current_dir}/nginx/Dockerfile"
33 |
34 | echo; echo "@> Restart and update containers"; echo;
35 |
36 | cd "${current_dir}" &&
37 | docker-compose down &&
38 | docker-compose up -d
39 |
--------------------------------------------------------------------------------
/scripts/test/index.js:
--------------------------------------------------------------------------------
1 | const $ = require('shelljs')
2 | require('../logging')
3 |
4 | $.config.fatal = true
5 |
6 | const unit_test_script = require('./unit_tests')
7 |
8 | try {
9 | unit_test_script.run_unit_tests('Release')
10 | } catch (e) {
11 | console.error(e)
12 | process.exit(1)
13 | }
14 |
15 | console.info(`Tests succeeded.`)
--------------------------------------------------------------------------------
/scripts/test/unit_tests.js:
--------------------------------------------------------------------------------
1 | const $ = require('shelljs')
2 | const path = require('path')
3 | require('../logging')
4 |
5 | $.config.fatal = true
6 | const root = path.join(__dirname, '..', '..')
7 |
8 |
9 | module.exports.run_unit_tests = function (configuration) {
10 | console.info(`Running unit tests with configuartion ${configuration}`)
11 |
12 | $.exec(
13 | `docker run --rm ` +
14 | `--volume "${root}:/project" ` +
15 | `--workdir /project/test/UnitTests.NetCore/ ` +
16 | `microsoft/dotnet:2.1.402-sdk ` +
17 | `dotnet test --configuration ${configuration} --framework netcoreapp2.1`
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/ASP.NET Core/TelegramBotMiddleware.cs:
--------------------------------------------------------------------------------
1 | #if !NETFRAMEWORK
2 |
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Logging;
6 | using Newtonsoft.Json;
7 | using System;
8 | using System.IO;
9 | using System.Threading.Tasks;
10 | using Telegram.Bot.Framework.Abstractions;
11 | using Telegram.Bot.Types;
12 |
13 | namespace Telegram.Bot.Framework
14 | {
15 | internal class TelegramBotMiddleware
16 | where TBot : BotBase
17 | {
18 | private readonly RequestDelegate _next;
19 |
20 | private readonly UpdateDelegate _updateDelegate;
21 |
22 | private readonly ILogger> _logger;
23 |
24 | ///
25 | /// Initializes an instance of middleware
26 | ///
27 | /// Instance of request delegate
28 | /// Logger for this middleware
29 | public TelegramBotMiddleware(
30 | RequestDelegate next,
31 | UpdateDelegate updateDelegate,
32 | ILogger> logger
33 | )
34 | {
35 | _next = next;
36 | _updateDelegate = updateDelegate;
37 | _logger = logger;
38 | }
39 |
40 | ///
41 | /// Gets invoked to handle the incoming request
42 | ///
43 | ///
44 | public async Task Invoke(HttpContext context)
45 | {
46 | if (context.Request.Method != HttpMethods.Post)
47 | {
48 | await _next.Invoke(context)
49 | .ConfigureAwait(false);
50 | return;
51 | }
52 |
53 | string payload;
54 | using (var reader = new StreamReader(context.Request.Body))
55 | {
56 | payload = await reader.ReadToEndAsync()
57 | .ConfigureAwait(false);
58 | }
59 |
60 | _logger.LogDebug("Update payload:\n{0}", payload);
61 |
62 | Update update = null;
63 | try
64 | {
65 | update = JsonConvert.DeserializeObject(payload);
66 | }
67 | catch (JsonException e)
68 | {
69 | _logger.LogError($"Unable to deserialize update payload. {e.Message}");
70 | }
71 |
72 | if (update == null)
73 | {
74 | // it is unlikely of Telegram to send an invalid update object.
75 | // respond with "404 Not Found" in case an attacker is trying to find the webhook URL
76 | context.Response.StatusCode = 404;
77 | return;
78 | }
79 |
80 | using (var scope = context.RequestServices.CreateScope())
81 | {
82 | var bot = scope.ServiceProvider.GetRequiredService();
83 | var updateContext = new UpdateContext(bot, update, scope.ServiceProvider);
84 | updateContext.Items.Add(nameof(HttpContext), context);
85 |
86 | try
87 | {
88 | await _updateDelegate(updateContext)
89 | .ConfigureAwait(false);
90 | }
91 | catch (Exception e)
92 | {
93 | _logger.LogError($"Error occured while handling update `{update.Id}`. {e.Message}");
94 | context.Response.StatusCode = StatusCodes.Status500InternalServerError;
95 | }
96 | }
97 |
98 | if (!context.Response.HasStarted)
99 | {
100 | context.Response.StatusCode = 201;
101 | }
102 | }
103 | }
104 | }
105 |
106 | #endif
107 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/ASP.NET Core/TelegramBotMiddlewareExtensions.cs:
--------------------------------------------------------------------------------
1 | #if !NETFRAMEWORK
2 |
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.Extensions.Options;
5 | using Telegram.Bot.Framework;
6 | using Telegram.Bot.Framework.Abstractions;
7 |
8 | namespace Microsoft.AspNetCore.Builder
9 | {
10 | ///
11 | /// Extenstion methods for adding Telegram Bot framework to the ASP.NET Core middleware
12 | ///
13 | public static class TelegramBotMiddlewareExtensions
14 | {
15 | ///
16 | /// Add Telegram bot webhook handling functionality to the pipeline
17 | ///
18 | /// Type of bot
19 | /// Instance of IApplicationBuilder
20 | /// Whether to set the webhook immediately by making a request to Telegram bot API
21 | /// Instance of IApplicationBuilder
22 | public static IApplicationBuilder UseTelegramBotWebhook(
23 | this IApplicationBuilder app,
24 | IBotBuilder botBuilder
25 | )
26 | where TBot : BotBase
27 | {
28 | var updateDelegate = botBuilder.Build();
29 |
30 | var options = app.ApplicationServices.GetRequiredService>>();
31 | app.Map(
32 | options.Value.WebhookPath,
33 | builder => builder.UseMiddleware>(updateDelegate)
34 | );
35 |
36 | return app;
37 | }
38 |
39 | /////
40 | ///// Removes and disables webhooks for bot
41 | /////
42 | ///// Type of bot
43 | ///// Instance of IApplicationBuilder
44 | ///// If true, a request is immediately made to delete webhook
45 | ///// Instance of IApplicationBuilder
46 | //public static IApplicationBuilder UseTelegramBotLongPolling(this IApplicationBuilder app, bool ensureWebhookDisabled = true)
47 | // where TBot : BotBase
48 | //{
49 | // IBotManager botManager = FindBotManager(app);
50 |
51 | // if (ensureWebhookDisabled)
52 | // {
53 | // botManager.SetWebhookStateAsync(false).Wait();
54 | // }
55 |
56 | // return app;
57 | //}
58 |
59 | /////
60 | ///// Add a Telegram game score middleware to the app
61 | /////
62 | ///// Type of bot
63 | ///// Instance of IApplicationBuilder
64 | ///// Instance of IApplicationBuilder
65 | //public static IApplicationBuilder UseTelegramGame(this IApplicationBuilder app)
66 | // where TBot : BotBase
67 | //{
68 | // app.UseMiddleware>();
69 |
70 | // return app;
71 | //}
72 |
73 | //private static IBotManager FindBotManager(IApplicationBuilder app)
74 | // where TBot : BotBase
75 | //{
76 | // IBotManager botManager;
77 | // try
78 | // {
79 | // botManager = app.ApplicationServices.GetRequiredService>();
80 | // if (botManager == null)
81 | // {
82 | // throw new NullReferenceException();
83 | // }
84 | // }
85 | // catch (Exception)
86 | // {
87 | // throw new ConfigurationException(
88 | // "Bot Manager service is not available", string.Format("Use services.{0}<{1}>()",
89 | // nameof(TelegramBotFrameworkIServiceCollectionExtensions.AddTelegramBot), typeof(TBot).Name));
90 | // }
91 | // return botManager;
92 | //}
93 | }
94 | }
95 |
96 | #endif
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IBot.cs:
--------------------------------------------------------------------------------
1 | namespace Telegram.Bot.Framework.Abstractions
2 | {
3 | ///
4 | /// A wrapper around TelegramBot class. Used to make calls to the Bot API
5 | ///
6 | public interface IBot
7 | {
8 | string Username { get; }
9 |
10 | ///
11 | /// Instance of Telegram bot client
12 | ///
13 | ITelegramBotClient Client { get; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IBotBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Telegram.Bot.Framework.Abstractions
4 | {
5 | public interface IBotBuilder
6 | {
7 | IBotBuilder Use(Func middleware);
8 |
9 | IBotBuilder Use(Func component);
10 |
11 | IBotBuilder Use()
12 | where THandler : IUpdateHandler;
13 |
14 | IBotBuilder Use(THandler handler)
15 | where THandler : IUpdateHandler;
16 |
17 | UpdateDelegate Build();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IBotOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Telegram.Bot.Framework.Abstractions
2 | {
3 | ///
4 | /// Configurations for the bot
5 | ///
6 | public interface IBotOptions
7 | {
8 | string Username { get; }
9 |
10 | ///
11 | /// Optional if client not needed. Telegram API token
12 | ///
13 | string ApiToken { get; }
14 |
15 | string WebhookPath { get; }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IBotServiceProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Telegram.Bot.Framework.Abstractions
4 | {
5 | public interface IBotServiceProvider : IServiceProvider, IDisposable
6 | {
7 | IBotServiceProvider CreateScope();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IUpdateContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Telegram.Bot.Types;
4 |
5 | namespace Telegram.Bot.Framework.Abstractions
6 | {
7 | public interface IUpdateContext
8 | {
9 | IBot Bot { get; }
10 |
11 | Update Update { get; }
12 |
13 | IServiceProvider Services { get; }
14 |
15 | IDictionary Items { get; }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IUpdateHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Telegram.Bot.Framework.Abstractions
4 | {
5 | ///
6 | /// Processes an update
7 | ///
8 | public interface IUpdateHandler
9 | {
10 | ///
11 | /// Handles the update for bot. This method will be called only if CanHandleUpdate returns true
12 | ///
13 | /// Instance of the bot this command is operating for
14 | /// The update to be handled
15 | /// Result of handling this update
16 | Task HandleAsync(IUpdateContext context, UpdateDelegate next);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/IUpdatePollingManager.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable UnusedTypeParameter
2 |
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Telegram.Bot.Requests;
6 |
7 | namespace Telegram.Bot.Framework.Abstractions
8 | {
9 | public interface IUpdatePollingManager
10 | where TBot : IBot
11 | {
12 | Task RunAsync(
13 | GetUpdatesRequest requestParams = default,
14 | CancellationToken cancellationToken = default
15 | );
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Abstractions/UpdateDelegate.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Telegram.Bot.Framework.Abstractions
4 | {
5 | public delegate Task UpdateDelegate(IUpdateContext context);
6 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/BotBase.cs:
--------------------------------------------------------------------------------
1 | using Telegram.Bot.Framework.Abstractions;
2 |
3 | namespace Telegram.Bot.Framework
4 | {
5 | public abstract class BotBase : IBot
6 | {
7 | public ITelegramBotClient Client { get; }
8 |
9 | public string Username { get; }
10 |
11 | protected BotBase(string username, ITelegramBotClient client)
12 | {
13 | Username = username;
14 | Client = client;
15 | }
16 |
17 | protected BotBase(string username, string token)
18 | : this(username, new TelegramBotClient(token))
19 | {
20 | }
21 |
22 | protected BotBase(IBotOptions options)
23 | : this(options.Username, new TelegramBotClient(options.ApiToken))
24 | {
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/BotOptions.cs:
--------------------------------------------------------------------------------
1 | using Telegram.Bot.Framework.Abstractions;
2 |
3 | namespace Telegram.Bot.Framework
4 | {
5 | ///
6 | /// Configurations for the bot
7 | ///
8 | /// Type of Bot
9 | public class BotOptions : IBotOptions
10 | where TBot : IBot
11 | {
12 | public string Username { get; set; }
13 |
14 | ///
15 | /// Optional if client not needed. Telegram API token
16 | ///
17 | public string ApiToken { get; set; }
18 |
19 | public string WebhookPath { get; set; }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/CommandBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text.RegularExpressions;
5 | using System.Threading.Tasks;
6 | using Telegram.Bot.Types;
7 | using Telegram.Bot.Types.Enums;
8 |
9 | namespace Telegram.Bot.Framework.Abstractions
10 | {
11 | ///
12 | /// Base handler implementation for a command such as "/start"
13 | ///
14 | public abstract class CommandBase : IUpdateHandler
15 | {
16 | public abstract Task HandleAsync(IUpdateContext context, UpdateDelegate next, string[] args);
17 |
18 | public Task HandleAsync(IUpdateContext context, UpdateDelegate next)
19 | {
20 | return HandleAsync(context, next, ParseCommandArgs(context.Update.Message));
21 | }
22 |
23 | public static string[] ParseCommandArgs(Message message)
24 | {
25 | if (message is null)
26 | throw new ArgumentNullException(nameof(message));
27 | if (message.Entities?.FirstOrDefault()?.Type != MessageEntityType.BotCommand)
28 | throw new ArgumentException("Message is not a command", nameof(message));
29 |
30 | var argsList = new List();
31 | string allArgs = message.Text.Substring(message.Entities[0].Length).TrimStart();
32 | argsList.Add(allArgs);
33 |
34 | var expandedArgs = Regex.Split(allArgs, @"\s+");
35 | if (expandedArgs.Length > 1)
36 | {
37 | argsList.AddRange(expandedArgs);
38 | }
39 |
40 | string[] args = argsList
41 | .Where(x => !string.IsNullOrWhiteSpace(x))
42 | .ToArray();
43 |
44 | return args;
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Extensions/BotExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text.RegularExpressions;
4 | using Telegram.Bot.Framework.Abstractions;
5 | using Telegram.Bot.Types;
6 | using Telegram.Bot.Types.Enums;
7 |
8 | namespace Telegram.Bot.Framework
9 | {
10 | public static class BotExtensions
11 | {
12 | public static bool CanHandleCommand(this IBot bot, string commandName, Message message)
13 | {
14 | if (string.IsNullOrWhiteSpace(commandName))
15 | throw new ArgumentException("Invalid command name", nameof(commandName));
16 | if (message == null)
17 | throw new ArgumentNullException(nameof(message));
18 | if (commandName.StartsWith("/"))
19 | throw new ArgumentException("Command name must not start with '/'.", nameof(commandName));
20 |
21 | {
22 | bool isTextMessage = message.Text != null;
23 | if (!isTextMessage)
24 | return false;
25 | }
26 |
27 | {
28 | bool isCommand = message.Entities?.FirstOrDefault()?.Type == MessageEntityType.BotCommand;
29 | if (!isCommand)
30 | return false;
31 | }
32 |
33 | return Regex.IsMatch(
34 | message.EntityValues.First(),
35 | $@"^/{commandName}(?:@{bot.Username})?$",
36 | RegexOptions.IgnoreCase
37 | );
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Telegram.Bot.Framework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net45;netstandard1.3
5 | Telegram.Bot.Framework
6 | 2.0.0-alpha4
7 |
8 | latest
9 | True
10 | True
11 |
12 | Telegram Bot Framework
13 | Simple framework for building powerful Telegram bots 🤖
14 | Poulad,TelegramBots
15 | telegram;bot;chatbot;chat-framework;framework;netcore
16 | false
17 | git
18 | https://github.com/TelegramBots/Telegram.Bot.Framework
19 | https://github.com/TelegramBots/Telegram.Bot.Framework.git
20 | https://raw.githubusercontent.com/TelegramBots/Telegram.Bot.Framework/master/docs/icon.png
21 | https://raw.githubusercontent.com/TelegramBots/Telegram.Bot.Framework/master/LICENSE
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Update Pipeline/BotBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Telegram.Bot.Framework.Abstractions;
6 |
7 | namespace Telegram.Bot.Framework
8 | {
9 | public class BotBuilder : IBotBuilder
10 | {
11 | internal UpdateDelegate UpdateDelegate { get; private set; }
12 |
13 | private readonly ICollection> _components;
14 |
15 | public BotBuilder()
16 | {
17 | _components = new List>();
18 | }
19 |
20 | public IBotBuilder Use(Func middleware)
21 | {
22 | throw new NotImplementedException();
23 | }
24 |
25 | public IBotBuilder Use()
26 | where THandler : IUpdateHandler
27 | {
28 | _components.Add(
29 | next =>
30 | context =>
31 | ((IUpdateHandler)context.Services.GetService(typeof(THandler)))
32 | .HandleAsync(context, next)
33 | );
34 |
35 | return this;
36 | }
37 |
38 | public IBotBuilder Use(THandler handler)
39 | where THandler : IUpdateHandler
40 | {
41 | _components.Add(next =>
42 | context => handler.HandleAsync(context, next)
43 | );
44 |
45 | return this;
46 | }
47 |
48 | public IBotBuilder Use(Func component)
49 | {
50 | throw new NotImplementedException();
51 | }
52 |
53 | public UpdateDelegate Build()
54 | {
55 | UpdateDelegate handle = context =>
56 | {
57 | // use Logger
58 | Console.WriteLine("No handler for update {0} of type {1}.", context.Update.Id, context.Update.Type);
59 | return Task.FromResult(1);
60 | };
61 |
62 | foreach (var component in _components.Reverse())
63 | {
64 | handle = component(handle);
65 | }
66 |
67 | return UpdateDelegate = handle;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Update Pipeline/BotBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Telegram.Bot.Framework.Abstractions;
3 | using Telegram.Bot.Framework.Pipeline;
4 |
5 | namespace Telegram.Bot.Framework
6 | {
7 | public static class BotBuilderExtensions
8 | {
9 | public static IBotBuilder UseWhen(
10 | this IBotBuilder builder,
11 | Predicate predicate,
12 | Action configure
13 | )
14 | {
15 | var branchBuilder = new BotBuilder();
16 | configure(branchBuilder);
17 | UpdateDelegate branchDelegate = branchBuilder.Build();
18 |
19 | builder.Use(new UseWhenMiddleware(predicate, branchDelegate));
20 |
21 | return builder;
22 | }
23 |
24 | public static IBotBuilder UseWhen(
25 | this IBotBuilder builder,
26 | Predicate predicate
27 | )
28 | where THandler : IUpdateHandler
29 | {
30 | var branchDelegate = new BotBuilder().Use().Build();
31 | builder.Use(new UseWhenMiddleware(predicate, branchDelegate));
32 | return builder;
33 | }
34 |
35 | public static IBotBuilder MapWhen(
36 | this IBotBuilder builder,
37 | Predicate predicate,
38 | Action configure
39 | )
40 | {
41 | var mapBuilder = new BotBuilder();
42 | configure(mapBuilder);
43 | UpdateDelegate mapDelegate = mapBuilder.Build();
44 |
45 | builder.Use(new MapWhenMiddleware(predicate, mapDelegate));
46 |
47 | return builder;
48 | }
49 |
50 | public static IBotBuilder MapWhen(
51 | this IBotBuilder builder,
52 | Predicate predicate
53 | )
54 | where THandler : IUpdateHandler
55 | {
56 | var branchDelegate = new BotBuilder().Use().Build();
57 | builder.Use(new MapWhenMiddleware(predicate, branchDelegate));
58 | return builder;
59 | }
60 |
61 | public static IBotBuilder UseCommand(
62 | this IBotBuilder builder,
63 | string command
64 | )
65 | where TCommand : CommandBase
66 | => builder
67 | .MapWhen(
68 | ctx => ctx.Bot.CanHandleCommand(command, ctx.Update.Message),
69 | botBuilder => botBuilder.Use()
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Update Pipeline/MapWhenMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Telegram.Bot.Framework.Pipeline
6 | {
7 | internal class MapWhenMiddleware : IUpdateHandler
8 | {
9 | private readonly Predicate _predicate;
10 |
11 | private readonly UpdateDelegate _branch;
12 |
13 | public MapWhenMiddleware(Predicate predicate, UpdateDelegate branch)
14 | {
15 | _predicate = predicate;
16 | _branch = branch;
17 | }
18 |
19 | public Task HandleAsync(IUpdateContext context, UpdateDelegate next)
20 | => _predicate(context)
21 | ? _branch(context)
22 | : next(context);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/Update Pipeline/UseWhenMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace Telegram.Bot.Framework.Pipeline
6 | {
7 | internal class UseWhenMiddleware : IUpdateHandler
8 | {
9 | private readonly Predicate _predicate;
10 |
11 | private readonly UpdateDelegate _branch;
12 |
13 | public UseWhenMiddleware(Predicate predicate, UpdateDelegate branch)
14 | {
15 | _predicate = predicate;
16 | _branch = branch;
17 | }
18 |
19 | public async Task HandleAsync(IUpdateContext context, UpdateDelegate next)
20 | {
21 | if (_predicate(context))
22 | {
23 | await _branch(context).ConfigureAwait(false);
24 | }
25 |
26 | await next(context).ConfigureAwait(false);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/UpdateContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using Telegram.Bot.Framework.Abstractions;
5 | using Telegram.Bot.Types;
6 |
7 | namespace Telegram.Bot.Framework
8 | {
9 | public class UpdateContext : IUpdateContext
10 | {
11 | public IBot Bot { get; }
12 |
13 | public Update Update { get; }
14 |
15 | public IServiceProvider Services { get; }
16 |
17 | public IDictionary Items { get; }
18 |
19 | public UpdateContext(IBot bot, Update u, IServiceProvider services)
20 | {
21 | Bot = bot;
22 | Update = u;
23 | Services = services;
24 | Items = new ConcurrentDictionary();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Telegram.Bot.Framework/UpdatePollingManager.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Requests;
5 | using Telegram.Bot.Types;
6 | using Telegram.Bot.Types.Enums;
7 |
8 | namespace Telegram.Bot.Framework
9 | {
10 | public class UpdatePollingManager : IUpdatePollingManager
11 | where TBot : IBot
12 | {
13 | private readonly UpdateDelegate _updateDelegate;
14 |
15 | private readonly IBotServiceProvider _rootProvider;
16 |
17 | public UpdatePollingManager(
18 | IBotBuilder botBuilder,
19 | IBotServiceProvider rootProvider
20 | )
21 | {
22 | // ToDo Receive update types array
23 | _updateDelegate = botBuilder.Build();
24 | _rootProvider = rootProvider;
25 | }
26 |
27 | public async Task RunAsync(
28 | GetUpdatesRequest requestParams = default,
29 | CancellationToken cancellationToken = default
30 | )
31 | {
32 | var bot = (TBot)_rootProvider.GetService(typeof(TBot));
33 |
34 | await bot.Client.DeleteWebhookAsync(cancellationToken)
35 | .ConfigureAwait(false);
36 |
37 | requestParams = requestParams ?? new GetUpdatesRequest
38 | {
39 | Offset = 0,
40 | Timeout = 500,
41 | AllowedUpdates = new UpdateType[0],
42 | };
43 |
44 | while (!cancellationToken.IsCancellationRequested)
45 | {
46 | Update[] updates = await bot.Client.MakeRequestAsync(
47 | requestParams,
48 | cancellationToken
49 | ).ConfigureAwait(false);
50 |
51 | foreach (var update in updates)
52 | {
53 | using (var scopeProvider = _rootProvider.CreateScope())
54 | {
55 | var context = new UpdateContext(bot, update, scopeProvider);
56 | // ToDo deep clone bot instance for each update
57 | await _updateDelegate(context)
58 | .ConfigureAwait(false);
59 | }
60 | }
61 |
62 | if (updates.Length > 0)
63 | {
64 | requestParams.Offset = updates[updates.Length - 1].Id + 1;
65 | }
66 | }
67 |
68 | cancellationToken.ThrowIfCancellationRequested();
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/test/UnitTests.Net45/Command Handling.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using System;
3 | using Telegram.Bot.Framework;
4 | using Telegram.Bot.Framework.Abstractions;
5 | using Telegram.Bot.Types;
6 | using Telegram.Bot.Types.Enums;
7 | using Xunit;
8 |
9 | namespace UnitTests.Net45
10 | {
11 | public class CommandHandling
12 | {
13 | [Theory(DisplayName = "Should accept handling valid \"/test\" commands for bot \"@Test_bot\"")]
14 | [InlineData("/test", "/test")]
15 | [InlineData("/test ", "/test")]
16 | [InlineData("/test abc", "/test")]
17 | [InlineData("/TesT", "/tESt")]
18 | [InlineData("/test@test_bot", "/test@test_bot")]
19 | [InlineData("/test@test_bot ", "/test@test_bot")]
20 | [InlineData("/test@test_bot !", "/test@test_bot")]
21 | public void Should_Parse_Valid_Commands(string text, string commandValue)
22 | {
23 | Mock mockBot = new Mock();
24 | mockBot.SetupGet(x => x.Username).Returns("Test_Bot");
25 |
26 | IBot bot = mockBot.Object;
27 | Message message = new Message
28 | {
29 | Text = text,
30 | Entities = new[]
31 | {
32 | new MessageEntity
33 | {
34 | Type = MessageEntityType.BotCommand,
35 | Offset = text.IndexOf(commandValue, StringComparison.OrdinalIgnoreCase),
36 | Length = commandValue.Length
37 | },
38 | },
39 | };
40 |
41 | bool result = bot.CanHandleCommand("test", message);
42 |
43 | Assert.True(result);
44 | }
45 |
46 | [Theory(DisplayName = "Should reject parsing non-command text messages as command \"/test\"")]
47 | [InlineData(null)]
48 | [InlineData(" ")]
49 | [InlineData("/\t")]
50 | [InlineData(" ")]
51 | [InlineData("I AM NOT A COMMAND")]
52 | [InlineData("/testt")]
53 | [InlineData("/@test_bot")]
54 | [InlineData("/tes@test_bot")]
55 | public void Should_Not_Parse_Invalid_Command_Text(string text)
56 | {
57 | Mock mockBot = new Mock();
58 | mockBot.SetupGet(x => x.Username).Returns("Test_Bot");
59 |
60 | IBot bot = mockBot.Object;
61 | Message message = new Message
62 | {
63 | Text = text,
64 | };
65 |
66 | bool result = bot.CanHandleCommand("test", message);
67 |
68 | Assert.False(result);
69 | }
70 |
71 | [Fact(DisplayName = "Should not accept handling non-text messages")]
72 | public void Should_Refuse_Handling_Non_Message_Updates()
73 | {
74 | Mock mockBot = new Mock();
75 | mockBot.SetupGet(x => x.Username).Returns("Test_Bot");
76 |
77 | IBot bot = mockBot.Object;
78 | Message message = new Message
79 | {
80 | Text = null,
81 | };
82 |
83 | bool result = bot.CanHandleCommand("test", message);
84 |
85 | Assert.False(result);
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/test/UnitTests.Net45/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("UnitTests.Net45")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("UnitTests.Net45")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("4e485e65-fbda-400b-9393-6e70c9e19c3d")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/test/UnitTests.Net45/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/UnitTests.Net45/packages.config:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/test/UnitTests.NetCore/Commands/Command Handling.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using System;
3 | using Telegram.Bot.Framework;
4 | using Telegram.Bot.Framework.Abstractions;
5 | using Telegram.Bot.Types;
6 | using Telegram.Bot.Types.Enums;
7 | using Xunit;
8 |
9 | namespace UnitTests.NetCore
10 | {
11 | public class CommandHandling
12 | {
13 | [Theory(DisplayName = "Should accept handling valid \"/test\" commands for bot \"@Test_bot\"")]
14 | [InlineData("/test", "/test")]
15 | [InlineData("/test ", "/test")]
16 | [InlineData("/test abc", "/test")]
17 | [InlineData("/TesT", "/tESt")]
18 | [InlineData("/test@test_bot", "/test@test_bot")]
19 | [InlineData("/test@test_bot ", "/test@test_bot")]
20 | [InlineData("/test@test_bot !", "/test@test_bot")]
21 | public void Should_Parse_Valid_Commands(string text, string commandValue)
22 | {
23 | Mock mockBot = new Mock();
24 | mockBot.SetupGet(x => x.Username).Returns("Test_Bot");
25 |
26 | IBot bot = mockBot.Object;
27 | Message message = new Message
28 | {
29 | Text = text,
30 | Entities = new[]
31 | {
32 | new MessageEntity
33 | {
34 | Type = MessageEntityType.BotCommand,
35 | Offset = text.IndexOf(commandValue, StringComparison.OrdinalIgnoreCase),
36 | Length = commandValue.Length
37 | },
38 | },
39 | };
40 |
41 | bool result = bot.CanHandleCommand("test", message);
42 |
43 | Assert.True(result);
44 | }
45 |
46 | [Theory(DisplayName = "Should reject parsing non-command text messages as command \"/test\"")]
47 | [InlineData(null)]
48 | [InlineData(" ")]
49 | [InlineData("/\t")]
50 | [InlineData(" ")]
51 | [InlineData("I AM NOT A COMMAND")]
52 | [InlineData("/testt")]
53 | [InlineData("/@test_bot")]
54 | [InlineData("/tes@test_bot")]
55 | public void Should_Not_Parse_Invalid_Command_Text(string text)
56 | {
57 | Mock mockBot = new Mock();
58 | mockBot.SetupGet(x => x.Username).Returns("Test_Bot");
59 |
60 | IBot bot = mockBot.Object;
61 | Message message = new Message
62 | {
63 | Text = text,
64 | };
65 |
66 | bool result = bot.CanHandleCommand("test", message);
67 |
68 | Assert.False(result);
69 | }
70 |
71 | [Fact(DisplayName = "Should not accept handling non-text messages")]
72 | public void Should_Refuse_Handling_Non_Message_Updates()
73 | {
74 | Mock mockBot = new Mock();
75 | mockBot.SetupGet(x => x.Username).Returns("Test_Bot");
76 |
77 | IBot bot = mockBot.Object;
78 | Message message = new Message
79 | {
80 | Text = null,
81 | };
82 |
83 | bool result = bot.CanHandleCommand("test", message);
84 |
85 | Assert.False(result);
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/test/UnitTests.NetCore/Commands/Command Parsing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Telegram.Bot.Framework.Abstractions;
4 | using Telegram.Bot.Types;
5 | using Xunit;
6 |
7 | namespace UnitTests.NetCore.Commands
8 | {
9 | public class ArgumentParsingTests
10 | {
11 | [Theory(DisplayName = "Should parse valid single command in a message")]
12 | [InlineData("/start")]
13 | [InlineData("/_")]
14 | [InlineData("/2")]
15 | [InlineData("/3_")]
16 | [InlineData("/start@ab3BOT")]
17 | [InlineData("/1@N_BoT")]
18 | public void Should_Parse_Valid_Commands(string command)
19 | {
20 | Message message = JsonConvert.DeserializeObject($@"{{
21 | message_id: 2,
22 | chat: {{ id: 333, type: ""private"" }},
23 | from: {{ id: 333, first_name: ""Poulad"", is_bot: false }},
24 | text: {JsonConvert.SerializeObject(command)},
25 | entities: [ {{ offset: 0, length: {command.Length}, type: ""bot_command"" }} ],
26 | date: 1000
27 | }}");
28 |
29 | string[] args = CommandBase.ParseCommandArgs(message);
30 |
31 | Assert.Empty(args);
32 | }
33 |
34 | [Theory(DisplayName = "Should parse valid a command with its arguments")]
35 | [InlineData("/1 bar", 2, "bar")]
36 | [InlineData("/foo bar baz", 4, "bar baz", "bar", "baz")]
37 | [InlineData("/foo\tbar baz", 4, "bar baz", "bar", "baz")]
38 | [InlineData("/foo\nbar baz", 4, "bar baz", "bar", "baz")]
39 | [InlineData("/_@9", 2, "@9")]
40 | [InlineData("/1-5 ab cd", 2, "-5 ab cd", "-5", "ab", "cd")]
41 | [InlineData("/1@T_Bot arg_1", 8, "arg_1")]
42 | [InlineData("/1@T_Bot arg_1 \"test\"", 8, "arg_1 \"test\"", "arg_1", @"""test""")]
43 | public void Should_Parse_Valid_Commands2(string text, int commandLength, params string[] expectedArgs)
44 | {
45 | Message message = JsonConvert.DeserializeObject($@"{{
46 | message_id: 2,
47 | chat: {{ id: 333, type: ""private"" }},
48 | from: {{ id: 333, first_name: ""Poulad"", is_bot: false }},
49 | text: {JsonConvert.SerializeObject(text)},
50 | entities: [ {{ offset: 0, length: {commandLength}, type: ""bot_command"" }} ],
51 | date: 1000
52 | }}");
53 |
54 | string[] args = CommandBase.ParseCommandArgs(message);
55 |
56 | Assert.Equal(expectedArgs, args);
57 | }
58 |
59 | [Fact(DisplayName = "Should throw exception if the message is null")]
60 | public void Should_Throw_When_Null_Message()
61 | {
62 | ArgumentNullException e = Assert.Throws(() =>
63 | CommandBase.ParseCommandArgs(null)
64 | );
65 |
66 | Assert.Equal("Value cannot be null.\nParameter name: message", e.Message);
67 | Assert.Equal("message", e.ParamName);
68 | }
69 |
70 | [Fact(DisplayName = "Should throw exception if the message does not have any entities")]
71 | public void Should_Throw_When_No_Message_Entity()
72 | {
73 | Message message = JsonConvert.DeserializeObject($@"{{
74 | message_id: 2,
75 | chat: {{ id: 333, type: ""private"" }},
76 | from: {{ id: 333, first_name: ""Poulad"", is_bot: false }},
77 | date: 1000
78 | }}");
79 |
80 | ArgumentException e = Assert.Throws(() =>
81 | CommandBase.ParseCommandArgs(message)
82 | );
83 |
84 | Assert.Equal("Message is not a command\nParameter name: message", e.Message);
85 | Assert.Equal("message", e.ParamName);
86 | }
87 |
88 | [Fact(DisplayName = "Should throw exception if the message entities array is empty")]
89 | public void Should_Throw_When_Empty_Message_Entities()
90 | {
91 | Message message = JsonConvert.DeserializeObject($@"{{
92 | message_id: 2,
93 | chat: {{ id: 333, type: ""private"" }},
94 | from: {{ id: 333, first_name: ""Poulad"", is_bot: false }},
95 | entities: [ ],
96 | date: 1000
97 | }}");
98 |
99 | ArgumentException e = Assert.Throws(() =>
100 | CommandBase.ParseCommandArgs(message)
101 | );
102 |
103 | Assert.Equal("Message is not a command\nParameter name: message", e.Message);
104 | Assert.Equal("message", e.ParamName);
105 | }
106 |
107 | [Fact(DisplayName = "Should throw exception if the first message entity is not a command")]
108 | public void Should_Throw_When_First_Message_Entity_Not_Command()
109 | {
110 | Message message = JsonConvert.DeserializeObject($@"{{
111 | message_id: 2,
112 | chat: {{ id: 333, type: ""private"" }},
113 | from: {{ id: 333, first_name: ""Poulad"", is_bot: false }},
114 | entities: [ {{ offset: 0, length: 4, type: ""bold"" }} ],
115 | date: 1000
116 | }}");
117 |
118 | ArgumentException e = Assert.Throws(() =>
119 | CommandBase.ParseCommandArgs(message)
120 | );
121 |
122 | Assert.Equal("Message is not a command\nParameter name: message", e.Message);
123 | Assert.Equal("message", e.ParamName);
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/test/UnitTests.NetCore/Commands/MockCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Telegram.Bot.Framework.Abstractions;
4 |
5 | namespace UnitTests.NetCore.Commands
6 | {
7 | class MockCommand : CommandBase
8 | {
9 | private readonly Func _handler;
10 |
11 | public MockCommand(Func handler)
12 | {
13 | _handler = handler;
14 | }
15 |
16 | public override Task HandleAsync(IUpdateContext context, UpdateDelegate next, string[] args)
17 | => _handler(context, next, args);
18 | }
19 | }
--------------------------------------------------------------------------------
/test/UnitTests.NetCore/UnitTests.NetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 | false
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/UnitTests.NetCore/test-update.json:
--------------------------------------------------------------------------------
1 | {
2 | "update_id": 594924625,
3 | "message": {
4 | "message_id": 17,
5 | "from": {
6 | "id": 195793677,
7 | "first_name": "Test Name",
8 | "username": "test_user"
9 | },
10 | "chat": {
11 | "id": 325426679,
12 | "first_name": "Test Name",
13 | "username": "test_user",
14 | "type": "private"
15 | },
16 | "date": 1494906408,
17 | "text": "hi"
18 | }
19 | }
--------------------------------------------------------------------------------