├── .editorconfig
├── .gitignore
├── .paket
├── .gitignore
└── paket.bootstrapper.exe
├── .vscode
├── launch.json
└── tasks.json
├── LICENSE.txt
├── Nuget.Config
├── README.md
├── TelegramClient.sln
├── TelegramClient.sln.DotSettings
├── build.fsx
├── build.ps1
├── paket.dependencies
├── paket.lock
├── src
└── TelegramClient.Core
│ ├── .editorconfig
│ ├── ApiServies
│ ├── AuthApiService.cs
│ ├── ConnectApiService.cs
│ ├── ContactsApiService.cs
│ ├── Interfaces
│ │ ├── IAuthApiService.cs
│ │ ├── IConnectApiService.cs
│ │ ├── IContactsApiService.cs
│ │ ├── IMessagesApiService.cs
│ │ ├── ISenderService.cs
│ │ ├── IUpdatesApiService.cs
│ │ └── IUploadApiService.cs
│ ├── MessagesApiService.cs
│ ├── SenderService.cs
│ ├── UpdatesApiService.cs
│ └── UploadApiService.cs
│ ├── Auth
│ ├── Step1_PQRequest.cs
│ ├── Step2_DHExchange.cs
│ └── Step3_CompleteDHExchange.cs
│ ├── Client.cs
│ ├── ClientFactory.cs
│ ├── Exceptions
│ └── MissingApiConfigurationException.cs
│ ├── FactorySettings.cs
│ ├── FodyWeavers.xml
│ ├── Helpers
│ └── BinaryHelper.cs
│ ├── ITelegramClient.cs
│ ├── IoC
│ ├── ComponentAttribute.cs
│ ├── CompositionExtensions.cs
│ ├── EDependencyLifecycle.cs
│ └── SingleInstanceAttribute.cs
│ ├── MTProto
│ ├── Crypto
│ │ ├── AES.cs
│ │ ├── AuthKey.cs
│ │ ├── BigInteger.cs
│ │ ├── Crc32.cs
│ │ ├── Factorizator.cs
│ │ ├── RSA.cs
│ │ └── Salt.cs
│ └── Serializers.cs
│ ├── Network
│ ├── Confirm
│ │ ├── ConfirmationSendService.cs
│ │ └── IConfirmationSendService.cs
│ ├── Exceptions
│ │ ├── AuthRestartException.cs
│ │ ├── BadServerSaltException.cs
│ │ ├── CloudPasswordNeededException.cs
│ │ ├── DataCenterMigrationException.cs
│ │ ├── DisconnectedException.cs
│ │ ├── FileMigrationException.cs
│ │ ├── FloodException.cs
│ │ ├── InvalidPhoneCodeException.cs
│ │ ├── PhoneMigrationException.cs
│ │ └── UserMigrationException.cs
│ ├── Interfaces
│ │ ├── IMtProtoPlainSender.cs
│ │ └── IMtProtoSender.cs
│ ├── Recieve
│ │ ├── GZipPackedHandler.cs
│ │ ├── Interfaces
│ │ │ ├── IGZipPackedHandler.cs
│ │ │ ├── IRecievingService.cs
│ │ │ ├── IResponseResultGetter.cs
│ │ │ └── IResponseResultSetter.cs
│ │ ├── RecievingService.cs
│ │ └── ResponseResultService.cs
│ ├── RecieveHandlers
│ │ ├── BadMsgNotificationRecieveHandler.cs
│ │ ├── BadServerSaltRecieveHandler.cs
│ │ ├── FutureSaltsRecieveHandler.cs
│ │ ├── Interfaces
│ │ │ └── IRecieveHandler.cs
│ │ ├── MsgDetailedInfoRecieveHandler.cs
│ │ ├── MsgNewDetailedInfoRecieveHandler.cs
│ │ ├── MsgsAckRecieveHandler.cs
│ │ ├── NewSessionCreatedRecieveHandler.cs
│ │ ├── PingRecieveHandler.cs
│ │ ├── PongRecieveHandler.cs
│ │ ├── RpcResultRecieveHandler.cs
│ │ └── UpdatesRecieveHandler.cs
│ ├── Send
│ │ ├── MtProtoPlainSender.cs
│ │ └── MtProtoSendService.cs
│ └── Tcp
│ │ ├── ITcpService.cs
│ │ ├── ITcpTransport.cs
│ │ ├── TcpMessage.cs
│ │ ├── TcpService.cs
│ │ └── TcpTransport.cs
│ ├── Sessions
│ ├── FileSessionStoreProvider.cs
│ ├── ISession.cs
│ ├── ISessionStore.cs
│ ├── ISessionStoreProvider.cs
│ ├── Session.cs
│ └── SessionStore.cs
│ ├── Settings
│ ├── ClientSettings.cs
│ └── IClientSettings.cs
│ ├── TelegramClient.Core.csproj
│ ├── TelegramClient.Core.csproj.DotSettings
│ └── Utils
│ ├── CRC32.cs
│ └── Helpers.cs
└── tests
├── TelegramClient.Tests
├── .gitignore
├── LogOutputTester.cs
├── TelegramClient.Tests.csproj
├── TelegramClientTests.cs
├── appsettings.json
├── data
│ └── cat.jpg
└── log4net.config
└── TelegramClient.UnitTests
├── Framework
├── ClientSettingsMock.cs
├── ConfirmationRecieveServiceMock.cs
├── ConfirmationSendServiceMock.cs
├── ContainerExtentions.cs
├── RecieveHandlerMock.cs
├── SessionMock.cs
├── SessionStoreMock.cs
├── TcpServiceMock.cs
├── TcpTransportMock.cs
└── TestBase.cs
├── Network
├── MtProtoPlainSenderTests.cs
├── MtProtoSendServiceTests.cs
├── RecieveServiceTests.cs
└── TcpTransportTests.cs
└── TelegramClient.UnitTests.csproj
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | end_of_line = crlf
3 |
4 | [*.{xml,cs}]
5 | indent_style = space
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/isua,visualstudio
2 |
3 | #!! ERROR: isua is undefined. Use list command to see defined gitignore types !!#
4 |
5 | ### VisualStudio ###
6 | ## Ignore Visual Studio temporary files, build results, and
7 | ## files generated by popular Visual Studio add-ons.
8 |
9 | # User-specific files
10 | *.suo
11 | *.user
12 | *.userosscache
13 | *.sln.docstates
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Build results
19 | [Dd]ebug/
20 | [Dd]ebugPublic/
21 | [Rr]elease/
22 | [Rr]eleases/
23 | x64/
24 | x86/
25 | build/
26 | bld/
27 | [Bb]in/
28 | [Oo]bj/
29 |
30 | # Visual Studio 2015 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # DNX
49 | project.lock.json
50 | artifacts/
51 |
52 | *_i.c
53 | *_p.c
54 | *_i.h
55 | *.ilk
56 | *.meta
57 | *.obj
58 | *.pch
59 | *.pdb
60 | *.pgc
61 | *.pgd
62 | *.rsp
63 | *.sbr
64 | *.tlb
65 | *.tli
66 | *.tlh
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opensdf
84 | *.sdf
85 | *.cachefile
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # NuGet Packages
149 | *.nupkg
150 | # The packages folder can be ignored because of Package Restore
151 | **/packages/*
152 | # except build/, which is used as an MSBuild target.
153 | !**/packages/build/
154 | # Uncomment if necessary however generally it will be regenerated when needed
155 | #!**/packages/repositories.config
156 |
157 | # Windows Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Windows Store app package directory
162 | AppPackages/
163 |
164 | # Visual Studio cache files
165 | # files ending in .cache can be ignored
166 | *.[Cc]ache
167 | # but keep track of directories ending in .cache
168 | !*.[Cc]ache/
169 |
170 | # Others
171 | ClientBin/
172 | [Ss]tyle[Cc]op.*
173 | ~$*
174 | *~
175 | *.dbmdl
176 | *.dbproj.schemaview
177 | *.pfx
178 | *.publishsettings
179 | node_modules/
180 | orleans.codegen.cs
181 |
182 | # RIA/Silverlight projects
183 | Generated_Code/
184 |
185 | # Backup & report files from converting an old project file
186 | # to a newer Visual Studio version. Backup files are not needed,
187 | # because we have git ;-)
188 | _UpgradeReport_Files/
189 | Backup*/
190 | UpgradeLog*.XML
191 | UpgradeLog*.htm
192 |
193 | # SQL Server files
194 | *.mdf
195 | *.ldf
196 |
197 | # Business Intelligence projects
198 | *.rdl.data
199 | *.bim.layout
200 | *.bim_*.settings
201 |
202 | # Microsoft Fakes
203 | FakesAssemblies/
204 |
205 | # Node.js Tools for Visual Studio
206 | .ntvs_analysis.dat
207 |
208 | # Visual Studio 6 build log
209 | *.plg
210 |
211 | # Visual Studio 6 workspace options file
212 | *.opt
213 |
214 | # Visual Studio LightSwitch build output
215 | **/*.HTMLClient/GeneratedArtifacts
216 | **/*.DesktopClient/GeneratedArtifacts
217 | **/*.DesktopClient/ModelManifest.xml
218 | **/*.Server/GeneratedArtifacts
219 | **/*.Server/ModelManifest.xml
220 | _Pvt_Extensions
221 | /packages/
222 | /.fake/
223 | /.idea/
224 |
--------------------------------------------------------------------------------
/.paket/.gitignore:
--------------------------------------------------------------------------------
1 | /paket.exe
2 |
--------------------------------------------------------------------------------
/.paket/paket.bootstrapper.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vik-borisov/TelegramClient/c967bd2f34e349529af7cbcc2ee11471a6fcbbba/.paket/paket.bootstrapper.exe
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": ".NET Core Launch (console)",
6 | "type": "coreclr",
7 | "request": "launch",
8 | "preLaunchTask": "build",
9 | "program": "${workspaceRoot}\\src\\TelegramClient.Core\\bin\\Debug\\netcoreapp1.1\\TelegramClient.Core.dll",
10 | "args": [],
11 | "cwd": "${workspaceRoot}\\src\\TelegramClient.Core",
12 | "console": "internalConsole",
13 | "stopAtEntry": false,
14 | "internalConsoleOptions": "openOnSessionStart"
15 | },
16 | {
17 | "name": ".NET Core Attach",
18 | "type": "coreclr",
19 | "request": "attach",
20 | "processId": "${command:pickProcess}"
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1.0",
3 | "command": "dotnet",
4 | "isShellCommand": true,
5 | "args": [],
6 | "tasks": [
7 | {
8 | "taskName": "build",
9 | "args": [
10 | "${workspaceRoot}\\src\\TelegramClient.Core\\TelegramClient.Core.csproj"
11 | ],
12 | "isBuildCommand": true,
13 | "problemMatcher": "$msCompile"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Ilya Pirozhenko
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Nuget.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TelegramClient DEPRECATED.
2 | # Instead, use https://github.com/OpenTl/OpenTl.ClientApi
3 |
4 | [](https://ci.appveyor.com/project/vik_borisov/telegramclient)
5 | [](https://badge.fury.io/nu/telegramclientapi)
6 | [](https://t.me/joinchat/D1EEGBGwdrHcoNbzXALYPg)
7 | [](https://www.codacy.com/app/OpenTl/TelegramClient?utm_source=github.com&utm_medium=referral&utm_content=vik-borisov/TelegramClient&utm_campaign=Badge_Grade)
8 |
9 | _Unofficial_ Telegram (http://telegram.org) client library implemented in C#. Latest TL scheme supported (73 layer). We used MtProto 2.0
10 |
11 | It's a perfect fit for any developer who would like to send data directly to Telegram users or write own custom Telegram client.
12 |
13 | :star2: If you :heart: library, please star it! :star2:
14 |
15 | # How do I add this to my project?
16 |
17 | Install via NuGet
18 |
19 | ```
20 | > Install-Package TelegramClientApi
21 | ```
22 | ## [Quick Start](https://github.com/vik-borisov/TelegramClient/wiki/Quick-Start)
23 |
24 | ## [Available Methods](https://github.com/vik-borisov/TelegramClient/wiki/Supported-methods)
25 |
26 | ## [Migrate to version 0.4](https://github.com/vik-borisov/TelegramClient/wiki/Migrate-to-version-0.4)
27 |
28 | ## [Schema Documentation](https://opentl.github.io/OpenTl.Schema/api/index.html)
29 |
30 | ## [Roadmap](https://github.com/vik-borisov/TelegramClient/milestones)
31 |
32 | ## [FAQ](https://github.com/vik-borisov/TelegramClient/wiki/FAQ)
33 |
34 | #### Nothing helps
35 | Ask your question at gitter or create an issue in project bug tracker.
36 |
37 | **Attach following information**:
38 |
39 | * Full problem description and exception message
40 | * Stack-trace
41 | * Your code that runs in to this exception
42 |
43 | Without information listen above your issue will be closed.
44 |
--------------------------------------------------------------------------------
/TelegramClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.12
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04D496F9-ECE3-4F26-ABF7-FD4511112142}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramClient.Core", "src\TelegramClient.Core\TelegramClient.Core.csproj", "{6DDAD7EF-AF3B-40EF-84DF-A364E83F9946}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{78830552-5E51-43BD-B510-7D59813B0CC9}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramClient.Tests", "tests\TelegramClient.Tests\TelegramClient.Tests.csproj", "{51FD7794-A861-439A-9CA3-75C4232E9CAC}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sln", "sln", "{679DFB1A-3BA6-450C-A42B-B6E3071EED37}"
15 | ProjectSection(SolutionItems) = preProject
16 | LICENSE.txt = LICENSE.txt
17 | README.md = README.md
18 | EndProjectSection
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 | {6DDAD7EF-AF3B-40EF-84DF-A364E83F9946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {6DDAD7EF-AF3B-40EF-84DF-A364E83F9946}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {6DDAD7EF-AF3B-40EF-84DF-A364E83F9946}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {6DDAD7EF-AF3B-40EF-84DF-A364E83F9946}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {51FD7794-A861-439A-9CA3-75C4232E9CAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {51FD7794-A861-439A-9CA3-75C4232E9CAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {51FD7794-A861-439A-9CA3-75C4232E9CAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {51FD7794-A861-439A-9CA3-75C4232E9CAC}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(NestedProjects) = preSolution
39 | {6DDAD7EF-AF3B-40EF-84DF-A364E83F9946} = {04D496F9-ECE3-4F26-ABF7-FD4511112142}
40 | {51FD7794-A861-439A-9CA3-75C4232E9CAC} = {78830552-5E51-43BD-B510-7D59813B0CC9}
41 | EndGlobalSection
42 | GlobalSection(ExtensibilityGlobals) = postSolution
43 | SolutionGuid = {7D2DDFF1-C8B9-45A6-98B7-94D467BAA2A9}
44 | EndGlobalSection
45 | GlobalSection(MonoDevelopProperties) = preSolution
46 | Policies = $0
47 | $0.TextStylePolicy = $1
48 | $1.inheritsSet = VisualStudio
49 | $1.inheritsScope = text/plain
50 | $1.scope = text/x-csharp
51 | $0.CSharpFormattingPolicy = $2
52 | $2.IndentSwitchBody = True
53 | $2.IndentBlocksInsideExpressions = True
54 | $2.AnonymousMethodBraceStyle = NextLine
55 | $2.PropertyBraceStyle = NextLine
56 | $2.PropertyGetBraceStyle = NextLine
57 | $2.PropertySetBraceStyle = NextLine
58 | $2.EventBraceStyle = NextLine
59 | $2.EventAddBraceStyle = NextLine
60 | $2.EventRemoveBraceStyle = NextLine
61 | $2.StatementBraceStyle = NextLine
62 | $2.ElseNewLinePlacement = NewLine
63 | $2.CatchNewLinePlacement = NewLine
64 | $2.FinallyNewLinePlacement = NewLine
65 | $2.WhileNewLinePlacement = DoNotCare
66 | $2.ArrayInitializerWrapping = DoNotChange
67 | $2.ArrayInitializerBraceStyle = NextLine
68 | $2.BeforeMethodDeclarationParentheses = False
69 | $2.BeforeMethodCallParentheses = False
70 | $2.BeforeConstructorDeclarationParentheses = False
71 | $2.NewLineBeforeConstructorInitializerColon = NewLine
72 | $2.NewLineAfterConstructorInitializerColon = SameLine
73 | $2.BeforeDelegateDeclarationParentheses = False
74 | $2.NewParentheses = False
75 | $2.SpacesBeforeBrackets = False
76 | $2.inheritsSet = Mono
77 | $2.inheritsScope = text/x-csharp
78 | $2.scope = text/x-csharp
79 | EndGlobalSection
80 | EndGlobal
81 |
--------------------------------------------------------------------------------
/build.fsx:
--------------------------------------------------------------------------------
1 | // include Fake lib
2 | #r "packages/FAKE/tools/FakeLib.dll"
3 | open Fake
4 |
5 | // Properties
6 | let buildDir = FullName "./build/"
7 |
8 | let apikey = getBuildParam "apikey"
9 | let version = getBuildParam "version"
10 |
11 | Target "Clean" (fun _ ->
12 | CleanDir buildDir
13 | )
14 |
15 | Target "Build" (fun _ ->
16 | XMLHelper.XmlPokeInnerText "./src/TelegramClient.Core/TelegramClient.Core.csproj" "/Project/PropertyGroup/Version" version
17 |
18 | DotNetCli.Restore (fun p -> p)
19 |
20 | DotNetCli.Build (fun p ->
21 | { p with
22 | Project = "./src/TelegramClient.Core/TelegramClient.Core.csproj"
23 | Configuration = "Release"
24 | })
25 |
26 | ()
27 |
28 | DotNetCli.Pack (fun p ->
29 | { p with
30 | OutputPath = buildDir
31 | Project = "./src/TelegramClient.Core/TelegramClient.Core.csproj"
32 | })
33 | )
34 |
35 | Target "PublishNuget" (fun _ ->
36 | Paket.Push (fun nugetParams ->
37 | { nugetParams with
38 | ApiKey = apikey
39 | WorkingDir = buildDir
40 | }
41 | )
42 | )
43 |
44 | Target "Default" (fun _ ->
45 | trace "Hello World from FAKE"
46 | )
47 |
48 | // Dependencies
49 | "Clean"
50 | ==> "Build"
51 | ==> "PublishNuget"
52 | ==> "Default"
53 |
54 | // start build
55 | RunTargetOrDefault "Default"
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [Parameter (Mandatory=$true)]
3 | [string] $version,
4 |
5 | [Parameter (Mandatory=$true)]
6 | [string] $apikey
7 | )
8 |
9 | function Check-Exit-Code{
10 | if ($LASTEXITCODE -eq 1) {
11 | exit /b $LASTEXITCODE
12 | }
13 | }
14 |
15 | &".\.paket\paket.bootstrapper.exe"
16 | Check-Exit-Code
17 |
18 | &".\.paket\paket.exe" "restore"
19 | Check-Exit-Code
20 |
21 | &".\packages\FAKE\tools\FAKE.exe" "build.fsx" "version=$version" "apikey=$apikey"
22 |
23 | Check-Exit-Code
--------------------------------------------------------------------------------
/paket.dependencies:
--------------------------------------------------------------------------------
1 | source https://www.nuget.org/api/v2
2 | nuget FAKE
3 |
--------------------------------------------------------------------------------
/paket.lock:
--------------------------------------------------------------------------------
1 | NUGET
2 | remote: https://www.nuget.org/api/v2
3 | FAKE (4.61.1)
4 |
--------------------------------------------------------------------------------
/src/TelegramClient.Core/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | end_of_line = crlf
3 |
4 | [*.{xml,cs} ]
5 | indent_style = space
6 |
7 | dotnet_naming_rule.private_members_with_underscore.symbols = private_members
8 | dotnet_naming_rule.private_members_with_underscore.style = underscore_prefix
9 | dotnet_naming_rule.private_members_with_underscore.severity = suggestion
10 |
11 | dotnet_naming_symbols.private_members.applicable_kinds = field
12 | dotnet_naming_symbols.private_members.applicable_accessibilities = private
13 | dotnet_naming_symbols.private_members.required_modifiers = readonly
14 |
15 | dotnet_naming_style.underscore_prefix.capitalization = camel_case
16 | dotnet_naming_style.underscore_prefix.required_prefix = _
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/AuthApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies
2 | {
3 | using System;
4 | using System.Linq;
5 | using System.Security.Cryptography;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | using BarsGroup.CodeGuard;
11 |
12 | using OpenTl.Schema;
13 | using OpenTl.Schema.Account;
14 | using OpenTl.Schema.Auth;
15 |
16 | using TelegramClient.Core.ApiServies.Interfaces;
17 | using TelegramClient.Core.IoC;
18 | using TelegramClient.Core.Sessions;
19 | using TelegramClient.Core.Settings;
20 |
21 | using TAuthorization = OpenTl.Schema.Auth.TAuthorization;
22 |
23 | [SingleInstance(typeof(IAuthApiService))]
24 | internal class AuthApiService : IAuthApiService
25 | {
26 | public ISenderService SenderService { get; set; }
27 |
28 | public IClientSettings ClientSettings { get; set; }
29 |
30 | public ISessionStore SessionStore { get; set; }
31 |
32 | public async Task GetPasswordSetting(CancellationToken cancellationToken = default(CancellationToken))
33 | {
34 | return await SenderService.SendRequestAsync(new RequestGetPassword(), cancellationToken).ConfigureAwait(false);
35 | }
36 |
37 | public async Task IsPhoneRegisteredAsync(string phoneNumber, CancellationToken cancellationToken = default(CancellationToken))
38 | {
39 | Guard.That(phoneNumber, nameof(phoneNumber)).IsNotNullOrWhiteSpace();
40 |
41 | var authCheckPhoneRequest = new RequestCheckPhone
42 | {
43 | PhoneNumber = phoneNumber
44 | };
45 | return await SenderService.SendRequestAsync(authCheckPhoneRequest, cancellationToken).ConfigureAwait(false);
46 | }
47 |
48 | public void EnsureUserAuthorized()
49 | {
50 | if (!IsUserAuthorized())
51 | {
52 | throw new InvalidOperationException("Authorize user first!");
53 | }
54 | }
55 |
56 | public bool IsUserAuthorized()
57 | {
58 | return ClientSettings.Session.User != null;
59 | }
60 |
61 | public async Task MakeAuthAsync(string phoneNumber, string phoneCodeHash, string code, CancellationToken cancellationToken = default(CancellationToken))
62 | {
63 | Guard.That(phoneNumber, nameof(phoneNumber)).IsNotNullOrWhiteSpace();
64 | Guard.That(phoneCodeHash, nameof(phoneCodeHash)).IsNotNullOrWhiteSpace();
65 | Guard.That(code, nameof(code)).IsNotNullOrWhiteSpace();
66 |
67 | var request = new RequestSignIn
68 | {
69 | PhoneNumber = phoneNumber,
70 | PhoneCodeHash = phoneCodeHash,
71 | PhoneCode = code
72 | };
73 |
74 | var result = (TAuthorization)await SenderService.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
75 |
76 | var user = result.User.Cast();
77 |
78 | await OnUserAuthenticated(user).ConfigureAwait(false);
79 |
80 | return user;
81 | }
82 |
83 | public async Task MakeAuthWithPasswordAsync(TPassword password, string passwordStr, CancellationToken cancellationToken = default(CancellationToken))
84 | {
85 | var passwordBytes = Encoding.UTF8.GetBytes(passwordStr);
86 | var rv = password.CurrentSalt.Concat(passwordBytes).Concat(password.CurrentSalt);
87 |
88 | byte[] passwordHash;
89 | using (var sha = SHA256.Create())
90 | {
91 | passwordHash = sha.ComputeHash(rv.ToArray());
92 | }
93 |
94 | var request = new RequestCheckPassword
95 | {
96 | PasswordHash = passwordHash
97 | };
98 | var result = (TAuthorization)await SenderService.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
99 |
100 | var user = result.User.As();
101 |
102 | await OnUserAuthenticated(user).ConfigureAwait(false);
103 |
104 | return user;
105 | }
106 |
107 | public async Task SendCodeRequestAsync(string phoneNumber, CancellationToken cancellationToken = default(CancellationToken))
108 | {
109 | Guard.That(phoneNumber, nameof(phoneNumber)).IsNotNullOrWhiteSpace();
110 |
111 | var request = new RequestSendCode
112 | {
113 | PhoneNumber = phoneNumber,
114 | ApiId = ClientSettings.AppId,
115 | ApiHash = ClientSettings.AppHash
116 | };
117 | return await SenderService.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
118 | }
119 |
120 | public async Task SignUpAsync(string phoneNumber, string phoneCodeHash, string code, string firstName, string lastName, CancellationToken cancellationToken = default(CancellationToken))
121 | {
122 | var request = new RequestSignUp
123 | {
124 | PhoneNumber = phoneNumber,
125 | PhoneCode = code,
126 | PhoneCodeHash = phoneCodeHash,
127 | FirstName = firstName,
128 | LastName = lastName
129 | };
130 | var result = (TAuthorization)await SenderService.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
131 |
132 | var user = result.User.Cast();
133 |
134 | await OnUserAuthenticated(user).ConfigureAwait(false);
135 | return user;
136 | }
137 |
138 | private async Task OnUserAuthenticated(TUser tlUser)
139 | {
140 | var session = ClientSettings.Session;
141 | Guard.That(session).IsNotNull();
142 |
143 | session.User = tlUser;
144 | session.SessionExpires = int.MaxValue;
145 |
146 | await SessionStore.Save().ConfigureAwait(false);
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/ConnectApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies
2 | {
3 | using System;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | using log4net;
9 |
10 | using OpenTl.Schema;
11 | using OpenTl.Schema.Auth;
12 | using OpenTl.Schema.Help;
13 |
14 | using TelegramClient.Core.ApiServies.Interfaces;
15 | using TelegramClient.Core.Auth;
16 | using TelegramClient.Core.IoC;
17 | using TelegramClient.Core.Network.Interfaces;
18 | using TelegramClient.Core.Network.Recieve.Interfaces;
19 | using TelegramClient.Core.Sessions;
20 | using TelegramClient.Core.Settings;
21 |
22 | [SingleInstance(typeof(IConnectApiService))]
23 | internal class ConnectApiService : IConnectApiService
24 | {
25 | private static readonly ILog Log = LogManager.GetLogger(typeof(ConnectApiService));
26 |
27 | private TDcOption[] _dcOptions;
28 |
29 | private Timer _timer;
30 |
31 | public IClientSettings ClientSettings { get; set; }
32 |
33 | public ISessionStore SessionStore { get; set; }
34 |
35 | public IMtProtoPlainSender MtProtoPlainSender { get; set; }
36 |
37 | public IRecievingService ProtoRecieveService { get; set; }
38 |
39 | public ISenderService SendService { get; set; }
40 |
41 | public Task ConnectAsync()
42 | {
43 | return ConnectAsync(false);
44 | }
45 |
46 | public async Task LogOut()
47 | {
48 | var request = new RequestLogOut();
49 | SendService.SendRequestAsync(request).ConfigureAwait(false);
50 |
51 | await SessionStore.Remove().ConfigureAwait(false);
52 | ClientSettings.Session.AuthKey = null;
53 | }
54 |
55 | public Task ReAuthenticateAsync()
56 | {
57 | return ConnectAsync(true);
58 | }
59 |
60 | public Task PingAsync()
61 | {
62 | return SendService.SendRequestAsync(new RequestPing());
63 | }
64 |
65 | public async Task ReconnectToDcAsync(int dcId)
66 | {
67 | if (_dcOptions == null || !_dcOptions.Any())
68 | {
69 | throw new InvalidOperationException($"Can't reconnect. Establish initial connection first.");
70 | }
71 |
72 | var dc = _dcOptions.First(d => d.Id == dcId);
73 |
74 | ClientSettings.Session.ServerAddress = dc.IpAddress;
75 | ClientSettings.Session.Port = dc.Port;
76 |
77 | await ConnectAsync(true).ConfigureAwait(false);
78 | }
79 |
80 | private async Task ConnectAsync(bool forceAuth)
81 | {
82 | if (ClientSettings.Session.AuthKey == null || ClientSettings.Session.AuthKey.Data.Length == 0 || forceAuth)
83 | {
84 | var result = await DoAuthentication().ConfigureAwait(false);
85 | ClientSettings.Session.AuthKey = result.AuthKey;
86 | ClientSettings.Session.TimeOffset = result.TimeOffset;
87 |
88 | await SessionStore.Save().ConfigureAwait(false);
89 | }
90 |
91 | ProtoRecieveService.StartReceiving();
92 |
93 |
94 | //set-up layer
95 | var request = new RequestInvokeWithLayer
96 | {
97 | Layer = SchemaInfo.SchemaVersion,
98 | Query = new RequestInitConnection
99 | {
100 | ApiId = ClientSettings.AppId,
101 | AppVersion = "1.0.0",
102 | DeviceModel = "PC",
103 | LangCode = "en",
104 | LangPack = "tdesktop",
105 | SystemLangCode = "en",
106 | Query = new RequestGetConfig(),
107 | SystemVersion = "Win 10.0"
108 | }
109 | };
110 |
111 | var response = (TConfig)await SendService.SendRequestAsync(request).ConfigureAwait(false);
112 | _dcOptions = response.DcOptions.Items.Cast().ToArray();
113 |
114 | _timer = new Timer(_ => PingAsync(), null, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
115 | }
116 |
117 | private async Task DoAuthentication()
118 | {
119 | Log.Info("Try do authentication");
120 |
121 | var step1 = new Step1PqRequest();
122 | var step1Result = await MtProtoPlainSender.SendAndReceive(step1.ToBytes()).ConfigureAwait(false);
123 | var step1Response = step1.FromBytes(step1Result);
124 |
125 | Log.Debug("First step is done");
126 |
127 | var step2 = new Step2DhExchange();
128 | var step2Result = await MtProtoPlainSender.SendAndReceive(
129 | step2.ToBytes(
130 | step1Response.Nonce,
131 | step1Response.ServerNonce,
132 | step1Response.Fingerprints,
133 | step1Response.Pq)).ConfigureAwait(false);
134 | var step2Response = step2.FromBytes(step2Result);
135 |
136 | Log.Debug("Second step is done");
137 |
138 | var step3 = new Step3CompleteDhExchange();
139 | var step3Result = await MtProtoPlainSender.SendAndReceive(
140 | step3.ToBytes(
141 | step2Response.Nonce,
142 | step2Response.ServerNonce,
143 | step2Response.NewNonce,
144 | step2Response.EncryptedAnswer))
145 | .ConfigureAwait(false);
146 | var step3Response = step3.FromBytes(step3Result);
147 |
148 | Log.Debug("Third step is done");
149 |
150 | return step3Response;
151 | }
152 |
153 | public void Dispose()
154 | {
155 | _timer?.Dispose();
156 | ProtoRecieveService?.Dispose();
157 | }
158 | }
159 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/ContactsApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema.Contacts;
7 |
8 | using TelegramClient.Core.ApiServies.Interfaces;
9 | using TelegramClient.Core.IoC;
10 |
11 | [SingleInstance(typeof(IContactsApiService))]
12 | internal class ContactsApiService : IContactsApiService
13 | {
14 | public IAuthApiService AuthApiService { get; set; }
15 |
16 | public ISenderService SenderService { get; set; }
17 |
18 | public async Task GetContactsAsync(CancellationToken cancellationToken = default(CancellationToken))
19 | {
20 | AuthApiService.EnsureUserAuthorized();
21 |
22 | var req = new RequestGetContacts { Hash = 0 };
23 |
24 | return await SenderService.SendRequestAsync(req, cancellationToken).ConfigureAwait(false);
25 | }
26 |
27 | /// Serch user or chat.
28 | /// User or chat name
29 | /// Max result count
30 | /// A cancellation token
31 | ///
32 | public async Task SearchUserAsync(string q, int limit = 10, CancellationToken cancellationToken = default(CancellationToken))
33 | {
34 | AuthApiService.EnsureUserAuthorized();
35 |
36 | var r = new RequestSearch
37 | {
38 | Q = q,
39 | Limit = limit
40 | };
41 |
42 | return await SenderService.SendRequestAsync(r, cancellationToken).ConfigureAwait(false);
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/Interfaces/IAuthApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies.Interfaces
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema;
7 | using OpenTl.Schema.Account;
8 | using OpenTl.Schema.Auth;
9 |
10 | public interface IAuthApiService
11 | {
12 | Task GetPasswordSetting(CancellationToken cancellationToken = default(CancellationToken));
13 |
14 | Task IsPhoneRegisteredAsync(string phoneNumber, CancellationToken cancellationToken = default(CancellationToken));
15 |
16 | bool IsUserAuthorized();
17 |
18 | void EnsureUserAuthorized();
19 |
20 | Task MakeAuthAsync(string phoneNumber, string phoneCodeHash, string code, CancellationToken cancellationToken = default(CancellationToken));
21 |
22 | Task MakeAuthWithPasswordAsync(TPassword password, string passwordStr, CancellationToken cancellationToken = default(CancellationToken));
23 |
24 | Task SendCodeRequestAsync(string phoneNumber, CancellationToken cancellationToken = default(CancellationToken));
25 |
26 | Task SignUpAsync(string phoneNumber, string phoneCodeHash, string code, string firstName, string lastName, CancellationToken cancellationToken = default(CancellationToken));
27 | }
28 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/Interfaces/IConnectApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies.Interfaces
2 | {
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema;
7 |
8 | public interface IConnectApiService: IDisposable
9 | {
10 | Task ConnectAsync();
11 |
12 | Task LogOut();
13 |
14 | Task PingAsync();
15 |
16 | Task ReAuthenticateAsync();
17 |
18 | Task ReconnectToDcAsync(int dcId);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/Interfaces/IContactsApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies.Interfaces
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema.Contacts;
7 |
8 | public interface IContactsApiService
9 | {
10 | Task GetContactsAsync(CancellationToken cancellationToken = default(CancellationToken));
11 |
12 | /// Serch user or chat. API: contacts.search#11f812d8 q:string limit:int = contacts.Found; By default the limit is
13 | /// 10.
14 | /// User or chat name
15 | /// Max result count
16 | ///
17 | Task SearchUserAsync(string q, int limit = 10, CancellationToken cancellationToken = default(CancellationToken));
18 | }
19 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/Interfaces/ISenderService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies.Interfaces
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema;
7 |
8 | public interface ISenderService
9 | {
10 | Task SendRequestAsync(IRequest methodToExecute, CancellationToken cancellationToken = default(CancellationToken));
11 | }
12 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/Interfaces/IUpdatesApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies.Interfaces
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema;
7 | using OpenTl.Schema.Updates;
8 |
9 | public delegate Task UpdateHandler(IUpdates update);
10 |
11 | public interface IUpdatesApiService
12 | {
13 | Task GetCurrentState(CancellationToken cancellationToken = default(CancellationToken));
14 |
15 | Task GetUpdates(IState currentState, CancellationToken cancellationToken = default(CancellationToken));
16 |
17 | event UpdateHandler RecieveUpdates;
18 | }
19 |
20 | internal interface IUpdatesApiServiceRaiser
21 | {
22 | Task OnUpdateRecieve(IUpdates message);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/Interfaces/IUploadApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies.Interfaces
2 | {
3 | using System.IO;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | using OpenTl.Schema;
8 | using OpenTl.Schema.Upload;
9 |
10 | public interface IUploadApiService
11 | {
12 | Task GetFile(IInputFileLocation location, int offset = 0, CancellationToken cancellationToken = default(CancellationToken));
13 |
14 | Task UploadFile(string name, StreamReader reader, CancellationToken cancellationToken = default(CancellationToken));
15 | }
16 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/SenderService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies
2 | {
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | using log4net;
8 |
9 | using OpenTl.Schema;
10 |
11 | using TelegramClient.Core.ApiServies.Interfaces;
12 | using TelegramClient.Core.IoC;
13 | using TelegramClient.Core.Network.Exceptions;
14 | using TelegramClient.Core.Network.Interfaces;
15 | using TelegramClient.Core.Network.Recieve.Interfaces;
16 |
17 | [SingleInstance(typeof(ISenderService))]
18 | internal class SenderService : ISenderService
19 | {
20 | private static readonly ILog Log = LogManager.GetLogger(typeof(SenderService));
21 |
22 | public IMtProtoSender Sender { get; set; }
23 |
24 | public async Task SendRequestAsync(IRequest methodToExecute, CancellationToken cancellationToken = default(CancellationToken))
25 | {
26 | while (true)
27 | {
28 | Log.Debug($"Send message of the constructor {methodToExecute}");
29 |
30 | try
31 | {
32 | return (TResult)await await Sender.SendAndWaitResponse(methodToExecute, cancellationToken).ConfigureAwait(false);
33 | }
34 | catch (BadServerSaltException)
35 | {
36 | }
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/UpdatesApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using OpenTl.Schema;
7 | using OpenTl.Schema.Updates;
8 |
9 | using TelegramClient.Core.ApiServies.Interfaces;
10 | using TelegramClient.Core.IoC;
11 |
12 | [SingleInstance(typeof(IUpdatesApiService), typeof(IUpdatesApiServiceRaiser))]
13 | internal class UpdatesApiService : IUpdatesApiService,
14 | IUpdatesApiServiceRaiser
15 | {
16 | public IAuthApiService AuthApiService { get; set; }
17 |
18 | public ISenderService SenderService { get; set; }
19 |
20 | public async Task GetCurrentState(CancellationToken cancellationToken = default(CancellationToken))
21 | {
22 | AuthApiService.EnsureUserAuthorized();
23 |
24 | return await SenderService.SendRequestAsync(new RequestGetState(), cancellationToken).ConfigureAwait(false);
25 | }
26 |
27 | public async Task GetUpdates(IState currentState, CancellationToken cancellationToken = default(CancellationToken))
28 | {
29 | AuthApiService.EnsureUserAuthorized();
30 |
31 | var getDiffRequest = new RequestGetDifference
32 | {
33 | Pts = currentState.Pts,
34 | Qts = currentState.Qts,
35 | Date = currentState.Date
36 | };
37 |
38 | return await SenderService.SendRequestAsync(getDiffRequest, cancellationToken).ConfigureAwait(false);
39 | }
40 |
41 | public async Task OnUpdateRecieve(IUpdates message)
42 | {
43 | if (RecieveUpdates != null)
44 | {
45 | await RecieveUpdates.Invoke(message).ConfigureAwait(false);
46 | }
47 | }
48 |
49 | public event UpdateHandler RecieveUpdates;
50 | }
51 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ApiServies/UploadApiService.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.ApiServies
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | using OpenTl.Schema;
12 | using OpenTl.Schema.Auth;
13 | using OpenTl.Schema.Upload;
14 |
15 | using TelegramClient.Core.ApiServies.Interfaces;
16 | using TelegramClient.Core.IoC;
17 | using TelegramClient.Core.Network.Exceptions;
18 | using TelegramClient.Core.Settings;
19 | using TelegramClient.Core.Utils;
20 |
21 | [SingleInstance(typeof(IUploadApiService))]
22 | internal class UploadApiService : IUploadApiService
23 | {
24 | private readonly int DownloadDocumentPartSize = 128 * 1024; // 128kb for document
25 |
26 | private readonly int DownloadPhotoPartSize = 64 * 1024; // 64kb for photo
27 |
28 | public ISenderService SenderService { get; set; }
29 |
30 | public IConnectApiService ConnectApiService { get; set; }
31 |
32 | public IClientSettings ClientSettings { get; set; }
33 |
34 | public IAuthApiService AuthApiService { get; set; }
35 |
36 | public async Task GetFile(IInputFileLocation location, int offset = 0, CancellationToken cancellationToken = default(CancellationToken))
37 | {
38 | AuthApiService.EnsureUserAuthorized();
39 |
40 | int filePartSize;
41 | if (location is TInputDocumentFileLocation)
42 | {
43 | filePartSize = DownloadDocumentPartSize;
44 | }
45 | else
46 | {
47 | filePartSize = DownloadPhotoPartSize;
48 | }
49 |
50 | try
51 | {
52 | return await SenderService.SendRequestAsync(
53 | new RequestGetFile
54 | {
55 | Location = location,
56 | Limit = filePartSize,
57 | Offset = offset
58 | },
59 | cancellationToken).ConfigureAwait(false);
60 | }
61 | catch (FileMigrationException ex)
62 | {
63 | var exportedAuth = (TExportedAuthorization)await SenderService.SendRequestAsync(
64 | new RequestExportAuthorization
65 | {
66 | DcId = ex.Dc
67 | },
68 | cancellationToken).ConfigureAwait(false);
69 |
70 | var authKey = ClientSettings.Session.AuthKey;
71 | var timeOffset = ClientSettings.Session.TimeOffset;
72 | var serverAddress = ClientSettings.Session.ServerAddress;
73 | var serverPort = ClientSettings.Session.Port;
74 |
75 | await ConnectApiService.ReconnectToDcAsync(ex.Dc).ConfigureAwait(false);
76 | await SenderService.SendRequestAsync(
77 | new RequestImportAuthorization
78 | {
79 | Bytes = exportedAuth.Bytes,
80 | Id = exportedAuth.Id
81 | },
82 | cancellationToken).ConfigureAwait(false);
83 | var result = await GetFile(location, offset, cancellationToken).ConfigureAwait(false);
84 |
85 | ClientSettings.Session.AuthKey = authKey;
86 | ClientSettings.Session.TimeOffset = timeOffset;
87 | ClientSettings.Session.ServerAddress = serverAddress;
88 | ClientSettings.Session.Port = serverPort;
89 | await ConnectApiService.ConnectAsync().ConfigureAwait(false);
90 |
91 | return result;
92 | }
93 | }
94 |
95 | public async Task UploadFile(string name, StreamReader reader, CancellationToken cancellationToken = default(CancellationToken))
96 | {
97 | AuthApiService.EnsureUserAuthorized();
98 |
99 | const long TenMb = 10 * 1024 * 1024;
100 | var isBigFileUpload = reader.BaseStream.Length >= TenMb;
101 |
102 | var file = await GetFile(reader).ConfigureAwait(false);
103 | var fileParts = GetFileParts(file);
104 |
105 | var partNumber = 0;
106 | var partsCount = fileParts.Count;
107 | var fileId = BitConverter.ToInt64(TlHelpers.GenerateRandomBytes(8), 0);
108 | while (fileParts.Count != 0)
109 | {
110 | var part = fileParts.Dequeue();
111 |
112 | if (isBigFileUpload)
113 | {
114 | await SenderService.SendRequestAsync(
115 | new RequestSaveBigFilePart
116 | {
117 | FileId = fileId,
118 | FilePart = partNumber,
119 | Bytes = part,
120 | FileTotalParts = partsCount
121 | },
122 | cancellationToken).ConfigureAwait(false);
123 | }
124 | else
125 | {
126 | await SenderService.SendRequestAsync(
127 | new RequestSaveFilePart
128 | {
129 | FileId = fileId,
130 | FilePart = partNumber,
131 | Bytes = part
132 | },
133 | cancellationToken).ConfigureAwait(false);
134 | }
135 |
136 | partNumber++;
137 | }
138 |
139 | if (isBigFileUpload)
140 | {
141 | return new TInputFileBig
142 | {
143 | Id = fileId,
144 | Name = name,
145 | Parts = partsCount
146 | };
147 | }
148 |
149 | return new TInputFile
150 | {
151 | Id = fileId,
152 | Name = name,
153 | Parts = partsCount,
154 | Md5Checksum = GetFileHash(file)
155 | };
156 | }
157 |
158 | private static async Task GetFile(StreamReader reader)
159 | {
160 | var file = new byte[reader.BaseStream.Length];
161 |
162 | using (reader)
163 | {
164 | await reader.BaseStream.ReadAsync(file, 0, (int)reader.BaseStream.Length).ConfigureAwait(false);
165 | }
166 |
167 | return file;
168 | }
169 |
170 | private static string GetFileHash(byte[] data)
171 | {
172 | string md5Checksum;
173 | using (var md5 = MD5.Create())
174 | {
175 | var hash = md5.ComputeHash(data);
176 | var hashResult = new StringBuilder(hash.Length * 2);
177 |
178 | foreach (var t in hash)
179 | {
180 | hashResult.Append(t.ToString("x2"));
181 | }
182 |
183 | md5Checksum = hashResult.ToString();
184 | }
185 |
186 | return md5Checksum;
187 | }
188 |
189 | private static Queue GetFileParts(byte[] file)
190 | {
191 | var fileParts = new Queue();
192 |
193 | const int MaxFilePart = 512 * 1024;
194 |
195 | using (var stream = new MemoryStream(file))
196 | {
197 | while (stream.Position != stream.Length)
198 | {
199 | if (stream.Length - stream.Position > MaxFilePart)
200 | {
201 | var temp = new byte[MaxFilePart];
202 | stream.Read(temp, 0, MaxFilePart);
203 | fileParts.Enqueue(temp);
204 | }
205 | else
206 | {
207 | var length = stream.Length - stream.Position;
208 | var temp = new byte[length];
209 | stream.Read(temp, 0, (int)length);
210 | fileParts.Enqueue(temp);
211 | }
212 | }
213 | }
214 |
215 | return fileParts;
216 | }
217 | }
218 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/Auth/Step1_PQRequest.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.Auth
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 |
8 | using TelegramClient.Core.MTProto;
9 | using TelegramClient.Core.MTProto.Crypto;
10 |
11 | public class Step1Response
12 | {
13 | public byte[] Nonce { get; set; }
14 |
15 | public byte[] ServerNonce { get; set; }
16 |
17 | public BigInteger Pq { get; set; }
18 |
19 | public List Fingerprints { get; set; }
20 | }
21 |
22 | public class Step1PqRequest
23 | {
24 | private readonly byte[] _nonce;
25 |
26 | public Step1PqRequest()
27 | {
28 | _nonce = new byte[16];
29 | }
30 |
31 | public Step1Response FromBytes(byte[] bytes)
32 | {
33 | var fingerprints = new List();
34 |
35 | using (var memoryStream = new MemoryStream(bytes, false))
36 | {
37 | using (var binaryReader = new BinaryReader(memoryStream))
38 | {
39 | const int responseConstructorNumber = 0x05162463;
40 | var responseCode = binaryReader.ReadInt32();
41 | if (responseCode != responseConstructorNumber)
42 | {
43 | throw new InvalidOperationException($"invalid response code: {responseCode}");
44 | }
45 |
46 | var nonceFromServer = binaryReader.ReadBytes(16);
47 |
48 | if (!nonceFromServer.SequenceEqual(_nonce))
49 | {
50 | throw new InvalidOperationException("invalid nonce from server");
51 | }
52 |
53 | var serverNonce = binaryReader.ReadBytes(16);
54 |
55 | var pqbytes = Serializers.Bytes.Read(binaryReader);
56 | var pq = new BigInteger(1, pqbytes);
57 |
58 | var vectorId = binaryReader.ReadInt32();
59 | const int vectorConstructorNumber = 0x1cb5c415;
60 | if (vectorId != vectorConstructorNumber)
61 | {
62 | throw new InvalidOperationException($"Invalid vector constructor number {vectorId}");
63 | }
64 |
65 | var fingerprintCount = binaryReader.ReadInt32();
66 | for (var i = 0; i < fingerprintCount; i++)
67 | {
68 | var fingerprint = binaryReader.ReadBytes(8);
69 | fingerprints.Add(fingerprint);
70 | }
71 |
72 | return new Step1Response
73 | {
74 | Fingerprints = fingerprints,
75 | Nonce = _nonce,
76 | Pq = pq,
77 | ServerNonce = serverNonce
78 | };
79 | }
80 | }
81 | }
82 |
83 | public byte[] ToBytes()
84 | {
85 | new Random().NextBytes(_nonce);
86 | const int ConstructorNumber = 0x60469778;
87 |
88 | using (var memoryStream = new MemoryStream())
89 | {
90 | using (var binaryWriter = new BinaryWriter(memoryStream))
91 | {
92 | binaryWriter.Write(ConstructorNumber);
93 | binaryWriter.Write(_nonce);
94 | }
95 |
96 | return memoryStream.ToArray();
97 | }
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/Auth/Step2_DHExchange.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.Auth
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 |
7 | using TelegramClient.Core.MTProto;
8 | using TelegramClient.Core.MTProto.Crypto;
9 |
10 | public class Step2Response
11 | {
12 | public byte[] Nonce { get; set; }
13 |
14 | public byte[] ServerNonce { get; set; }
15 |
16 | public byte[] NewNonce { get; set; }
17 |
18 | public byte[] EncryptedAnswer { get; set; }
19 | }
20 |
21 | public class Step2DhExchange
22 | {
23 | private readonly byte[] _newNonce;
24 |
25 | public Step2DhExchange()
26 | {
27 | _newNonce = new byte[32];
28 | }
29 |
30 | public Step2Response FromBytes(byte[] response)
31 | {
32 | using (var responseStream = new MemoryStream(response, false))
33 | {
34 | using (var responseReader = new BinaryReader(responseStream))
35 | {
36 | var responseCode = responseReader.ReadUInt32();
37 |
38 | if (responseCode == 0x79cb045d)
39 | {
40 | throw new InvalidOperationException("server_DH_params_fail: TODO");
41 | }
42 |
43 | if (responseCode != 0xd0e8075c)
44 | {
45 | throw new InvalidOperationException($"invalid response code: {responseCode}");
46 | }
47 |
48 | var nonceFromServer = responseReader.ReadBytes(16);
49 |
50 | // TODO:!
51 | /*
52 | if (!nonceFromServer.SequenceEqual(nonce))
53 | {
54 | logger.debug("invalid nonce from server");
55 | return null;
56 | }
57 | */
58 |
59 | var serverNonceFromServer = responseReader.ReadBytes(16);
60 |
61 | // TODO: !
62 | /*
63 | if (!serverNonceFromServer.SequenceEqual(serverNonce))
64 | {
65 | logger.error("invalid server nonce from server");
66 | return null;
67 | }
68 | */
69 |
70 | var encryptedAnswer = Serializers.Bytes.Read(responseReader);
71 |
72 | return new Step2Response
73 | {
74 | EncryptedAnswer = encryptedAnswer,
75 | ServerNonce = serverNonceFromServer,
76 | Nonce = nonceFromServer,
77 | NewNonce = _newNonce
78 | };
79 | }
80 | }
81 | }
82 |
83 | public byte[] ToBytes(byte[] nonce, byte[] serverNonce, List fingerprints, BigInteger pq)
84 | {
85 | new Random().NextBytes(_newNonce);
86 |
87 | var pqPair = Factorizator.Factorize(pq);
88 |
89 | byte[] reqDhParamsBytes;
90 |
91 | using (var pqInnerData = new MemoryStream(255))
92 | {
93 | using (var pqInnerDataWriter = new BinaryWriter(pqInnerData))
94 | {
95 | pqInnerDataWriter.Write(0x83c95aec); // pq_inner_data
96 | Serializers.Bytes.Write(pqInnerDataWriter, pq.ToByteArrayUnsigned());
97 | Serializers.Bytes.Write(pqInnerDataWriter, pqPair.Min.ToByteArrayUnsigned());
98 | Serializers.Bytes.Write(pqInnerDataWriter, pqPair.Max.ToByteArrayUnsigned());
99 | pqInnerDataWriter.Write(nonce);
100 | pqInnerDataWriter.Write(serverNonce);
101 | pqInnerDataWriter.Write(_newNonce);
102 |
103 | byte[] ciphertext = null;
104 | byte[] targetFingerprint = null;
105 | foreach (var fingerprint in fingerprints)
106 | {
107 | pqInnerData.TryGetBuffer(out var buffer);
108 | ciphertext = Rsa.Encrypt(BitConverter.ToString(fingerprint).Replace("-", string.Empty), buffer.Array, 0, (int)pqInnerData.Position);
109 | if (ciphertext != null)
110 | {
111 | targetFingerprint = fingerprint;
112 | break;
113 | }
114 | }
115 |
116 | if (ciphertext == null)
117 | {
118 | throw new InvalidOperationException(
119 | string.Format(
120 | "not found valid key for fingerprints: {0}",
121 | string.Join(", ", fingerprints)));
122 | }
123 |
124 | using (var reqDhParams = new MemoryStream(1024))
125 | {
126 | using (var reqDhParamsWriter = new BinaryWriter(reqDhParams))
127 | {
128 | reqDhParamsWriter.Write(0xd712e4be); // req_dh_params
129 | reqDhParamsWriter.Write(nonce);
130 | reqDhParamsWriter.Write(serverNonce);
131 | Serializers.Bytes.Write(reqDhParamsWriter, pqPair.Min.ToByteArrayUnsigned());
132 | Serializers.Bytes.Write(reqDhParamsWriter, pqPair.Max.ToByteArrayUnsigned());
133 | reqDhParamsWriter.Write(targetFingerprint);
134 | Serializers.Bytes.Write(reqDhParamsWriter, ciphertext);
135 |
136 | reqDhParamsBytes = reqDhParams.ToArray();
137 | }
138 | }
139 | }
140 |
141 | return reqDhParamsBytes;
142 | }
143 | }
144 | }
145 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/Client.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core
2 | {
3 | using System;
4 |
5 | using Castle.Windsor;
6 |
7 | using TelegramClient.Core.ApiServies.Interfaces;
8 | using TelegramClient.Core.IoC;
9 |
10 | [SingleInstance(typeof(ITelegramClient))]
11 | internal class Client : ITelegramClient
12 | {
13 | public ISenderService SendService { get; set; }
14 |
15 | public IUpdatesApiService UpdatesService { get; set; }
16 |
17 | public IConnectApiService ConnectService { get; set; }
18 |
19 | public IAuthApiService AuthService { get; set; }
20 |
21 | public IMessagesApiService MessagesService { get; set; }
22 |
23 | public IContactsApiService ContactsService { get; set; }
24 |
25 | public IUploadApiService UploadService { get; set; }
26 |
27 | private IWindsorContainer Container { get; set; }
28 |
29 | public void Dispose()
30 | {
31 | Container?.Dispose();
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ClientFactory.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Threading.Tasks;
8 |
9 | using BarsGroup.CodeGuard;
10 |
11 | using Castle.MicroKernel.Registration;
12 | using Castle.Windsor;
13 |
14 | using TelegramClient.Core.IoC;
15 | using TelegramClient.Core.Network.RecieveHandlers.Interfaces;
16 | using TelegramClient.Core.Sessions;
17 | using TelegramClient.Core.Settings;
18 |
19 | public static class ClientFactory
20 | {
21 | public static async Task BuildClient(IFactorySettings factorySettings)
22 | {
23 | var container = RegisterDependency();
24 |
25 | await FillSettings(container, factorySettings).ConfigureAwait(false);
26 |
27 | return container.Resolve();
28 | }
29 |
30 | private static async Task FillSettings(IWindsorContainer container, IFactorySettings factorySettings)
31 | {
32 | Guard.That(factorySettings.Id).IsPositive();
33 | Guard.That(factorySettings.Hash).IsNotNullOrWhiteSpace();
34 | Guard.That(factorySettings.ServerAddress).IsNotNullOrWhiteSpace();
35 | Guard.That(factorySettings.ServerPort).IsPositive();
36 |
37 | var settings = container.Resolve();
38 |
39 | settings.AppId = factorySettings.Id;
40 | settings.AppHash = factorySettings.Hash;
41 |
42 | container.Register(Component.For().Instance(factorySettings.StoreProvider));
43 |
44 | var sessionStore = container.Resolve();
45 |
46 | settings.Session = await TryLoadOrCreateNew(sessionStore, factorySettings).ConfigureAwait(false);
47 | }
48 |
49 | private static IWindsorContainer RegisterDependency()
50 | {
51 | var container = new WindsorContainer();
52 |
53 | container.RegisterAttibuteRegistration(typeof(ClientFactory).GetTypeInfo().Assembly);
54 |
55 | container.Register(
56 | Component.For>().UsingFactoryMethod(
57 | kernel =>
58 | {
59 | var handlerMap = new Dictionary();
60 |
61 | var allHandlers = kernel.ResolveAll();
62 | foreach (var handler in allHandlers.ToArray())
63 | {
64 | foreach (var handleCode in handler.HandleCodes)
65 | {
66 | handlerMap[handleCode] = handler;
67 | }
68 | }
69 |
70 | return handlerMap;
71 | }));
72 |
73 | return container;
74 | }
75 |
76 | private static async Task TryLoadOrCreateNew(ISessionStore sessionStore, IFactorySettings factorySettings)
77 | {
78 | var session = await sessionStore.Load().ConfigureAwait(false) ?? Session.Create();
79 |
80 | session.ServerAddress = factorySettings.ServerAddress;
81 | session.Port = factorySettings.ServerPort;
82 |
83 | return session;
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/Exceptions/MissingApiConfigurationException.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.Exceptions
2 | {
3 | using System;
4 |
5 | public class MissingApiConfigurationException : Exception
6 | {
7 | public const string InfoUrl = "https://github.com/sochix/TLSharp#quick-configuration";
8 |
9 | internal MissingApiConfigurationException(string invalidParamName) :
10 | base($"Your {invalidParamName} setting is missing. Adjust the configuration first, see {InfoUrl}")
11 | {
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/FactorySettings.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core
2 | {
3 | using TelegramClient.Core.Sessions;
4 |
5 | public interface IFactorySettings
6 | {
7 | int Id { get; set; }
8 |
9 | string Hash { get; set; }
10 |
11 | string ServerAddress { get; set; }
12 |
13 | int ServerPort { get; set; }
14 |
15 | ISessionStoreProvider StoreProvider { get; set; }
16 | }
17 |
18 | public class FactorySettings : IFactorySettings
19 | {
20 | public int Id { get; set; }
21 |
22 | public string Hash { get; set; }
23 |
24 | public string ServerAddress { get; set; }
25 |
26 | public int ServerPort { get; set; }
27 |
28 | public ISessionStoreProvider StoreProvider { get; set; }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/TelegramClient.Core/Helpers/BinaryHelper.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.Helpers
2 | {
3 | using System;
4 | using System.IO;
5 |
6 | internal static class BinaryHelper
7 | {
8 | public static byte[] WriteBytes(Action action)
9 | {
10 | using (var memoryStream = new MemoryStream())
11 | {
12 | using (var binaryWriter = new BinaryWriter(memoryStream))
13 | {
14 | action(binaryWriter);
15 | }
16 |
17 | return memoryStream.ToArray();
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/ITelegramClient.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("TelegramClient.UnitTests")]
4 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
5 |
6 | namespace TelegramClient.Core
7 | {
8 | using System;
9 |
10 | using TelegramClient.Core.ApiServies.Interfaces;
11 |
12 | public interface ITelegramClient : IDisposable
13 | {
14 | /// Send custom messages
15 | ISenderService SendService { get; }
16 |
17 | /// Automatic and manual updates
18 | IUpdatesApiService UpdatesService { get; }
19 |
20 | /// Connecting to the server
21 | IConnectApiService ConnectService { get; }
22 |
23 | /// Registration and authentication
24 | IAuthApiService AuthService { get; }
25 |
26 | /// Messages and chats
27 | IMessagesApiService MessagesService { get; }
28 |
29 | /// Working with contacts
30 | IContactsApiService ContactsService { get; }
31 |
32 | /// Working with files
33 | IUploadApiService UploadService { get; }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/IoC/ComponentAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.IoC
2 | {
3 | using System;
4 |
5 | using BarsGroup.CodeGuard;
6 |
7 | ///
8 | /// Marks the decorated class as a component that will be available from the service locator / component
9 | /// container.
10 | ///
11 | [AttributeUsage(AttributeTargets.Class)]
12 | public abstract class ComponentAttribute : Attribute
13 | {
14 | public EDependencyLifecycle Lifecycle { get; }
15 |
16 | /// Type to use for the component registration.
17 | public Type[] RegisterAs { get; }
18 |
19 | ///
20 | /// Initializes a new instance of the class, marking the decorated class as a
21 | /// component that will be available from the service locator / component container using the specified
22 | /// type.
23 | ///
24 | /// The type to use to register the decorated component.
25 | /// The lifecycle of dependency
26 | protected ComponentAttribute(Type[] registerAs, EDependencyLifecycle lifecycle)
27 | {
28 | Guard.That(registerAs).IsNotEmpty();
29 |
30 | RegisterAs = registerAs;
31 | Lifecycle = lifecycle;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/TelegramClient.Core/IoC/CompositionExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace TelegramClient.Core.IoC
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 |
8 | using Castle.MicroKernel.Registration;
9 | using Castle.Windsor;
10 |
11 | ///
12 | /// Provides automatic component registration by scanning assemblies and types for those that have the
13 | /// annotation.
14 | ///
15 | public static class CompositionExtensions
16 | {
17 | /// Registers the components found in the given assemblies.
18 | internal static void RegisterAttibuteRegistration(this IWindsorContainer builder, params Assembly[] assemblies)
19 | {
20 | RegisterComponents(builder, assemblies.SelectMany(x => x.GetTypes()));
21 | }
22 |
23 | /// Registers the components found in the given set of types.
24 | private static void RegisterComponents(this IWindsorContainer builder, IEnumerable types)
25 | {
26 | var supportedTypes = types.Where(t => t.GetTypeInfo().GetCustomAttribute(true) != null).ToArray();
27 |
28 | foreach (var type in supportedTypes)
29 | {
30 | var attibute = type.GetTypeInfo().GetCustomAttribute(true);
31 | builder.Register(Component.For(attibute.RegisterAs).ImplementedBy(type).RegisterComponents(attibute.Lifecycle));
32 | }
33 | }
34 |
35 | private static ComponentRegistration