From d7770bd626258fb0b9b719f5205967c5bda73d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20=C5=BBaba?= Date: Sun, 13 Dec 2020 22:36:19 +0100 Subject: [PATCH] Initial commit --- Strava.sln | 25 +++++ Strava/App.config | 19 ++++ Strava/Program.cs | 167 ++++++++++++++++++++++++++++++ Strava/Properties/AssemblyInfo.cs | 36 +++++++ Strava/Strava.csproj | 62 +++++++++++ Strava/packages.config | 5 + 6 files changed, 314 insertions(+) create mode 100644 Strava.sln create mode 100644 Strava/App.config create mode 100644 Strava/Program.cs create mode 100644 Strava/Properties/AssemblyInfo.cs create mode 100644 Strava/Strava.csproj create mode 100644 Strava/packages.config diff --git a/Strava.sln b/Strava.sln new file mode 100644 index 0000000..4aa5fdc --- /dev/null +++ b/Strava.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1231 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Strava", "Strava\Strava.csproj", "{1D30A9D5-4481-49EC-ABEC-C458AE41013B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1D30A9D5-4481-49EC-ABEC-C458AE41013B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D30A9D5-4481-49EC-ABEC-C458AE41013B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D30A9D5-4481-49EC-ABEC-C458AE41013B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D30A9D5-4481-49EC-ABEC-C458AE41013B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4A629EB7-EA5A-4E1E-BE83-C244640BB2C4} + EndGlobalSection +EndGlobal diff --git a/Strava/App.config b/Strava/App.config new file mode 100644 index 0000000..c04f628 --- /dev/null +++ b/Strava/App.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Strava/Program.cs b/Strava/Program.cs new file mode 100644 index 0000000..d6ab7e3 --- /dev/null +++ b/Strava/Program.cs @@ -0,0 +1,167 @@ +using Newtonsoft.Json; +using Strava.Api; +using Strava.Authentication; +using Strava.Clients; +using Strava.Upload; +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace Strava +{ + class Program + { + static string ClientId { get; set; } + static string ClientSecret { get; set; } + static string FolderWithTcx { get; set; } + + //You can also provide it here, if you don't want to mess around with UI + static string AuthCode = string.Empty; + + static void Main(string[] args) + { + #region settings + + if (!ReadClientSettings()) + { + //prompt and save to file + ClientId = ShowInputBox("Provide your Strava Client ID", "Configuration"); + ClientSecret = ShowInputBox("Provide your Strava Client Secret", "Configuration"); + FolderWithTcx = ShowInputBox("Provide path to folder with TCX files", "Configuration"); + + if (string.IsNullOrEmpty(ClientId) || string.IsNullOrEmpty(ClientSecret)) + { + Environment.Exit(0); + } + SetSetting(GetPropertyName(() => ClientId), ClientId); + SetSetting(GetPropertyName(() => ClientSecret), ClientSecret); + SetSetting(GetPropertyName(() => FolderWithTcx), FolderWithTcx); + } + Console.WriteLine("Configuration values - OK"); + #endregion + + #region authentication + + System.Diagnostics.Process.Start( + $"https://www.strava.com/oauth/mobile/authorize?client_id={ClientId}&redirect_uri=http%3A%2F%2Flocalhost&response_type=code&approval_prompt=auto&scope=activity%3Awrite%2Cread&state=test"); + + if (string.IsNullOrEmpty(AuthCode)) + { + AuthCode = ShowInputBox("Provide your Strava Auth Code","Configuration"); + } + + #endregion + + string[] files = Directory.GetFiles(FolderWithTcx, "*.tcx"); + Console.WriteLine($"Found {files.Count()} TCX files to process."); + + HttpClient httpClient = new HttpClient(); + + var values = new Dictionary + { + { "client_id", ClientId }, + { "client_secret", ClientSecret }, + { "code", AuthCode }, + { "grant_type", "authorization_code" } + }; + + try + { + var content = new FormUrlEncodedContent(values); + var response = httpClient.PostAsync("https://www.strava.com/api/v3/oauth/token", content).Result; + var responseString = response.Content.ReadAsStringAsync().Result; + dynamic objects = JsonConvert.DeserializeObject(responseString); + string token = objects["access_token"]; + if (!string.IsNullOrEmpty(token)) + Console.WriteLine("Authentication - OK"); + else Console.WriteLine("Authentication - FAILURE"); + + StaticAuthentication auth = new StaticAuthentication(token); + StravaClient client = new StravaClient(auth); + + Console.WriteLine("If you see nothing below for a longer period - try again after 15 minutes."); + + foreach (var item in files) + { + var task = + Task.Run(async () => + { + UploadStatus status = await client.Uploads.UploadActivityAsync(item, DataFormat.Tcx); + Thread.Sleep(2000); + UploadStatusCheck check = new UploadStatusCheck(token, status.Id.ToString()); + + check.UploadChecked += delegate (object o, UploadStatusCheckedEventArgs args2) + { + Console.WriteLine($"{args2.Status} {item} || Limit {Limits.Limit.ShortTerm} {Limits.Usage.ShortTerm}"); + if (args2.Status == CurrentUploadStatus.Ready) + { + File.Delete(item); + } + }; + + check.Start(); + }); + + task.Wait(); + } + } + catch(Exception ex) + { + Console.WriteLine(ex); + Console.Read(); + } + } + + private static bool ReadClientSettings() + { + ClientId = GetSetting(GetPropertyName(() => ClientId)); + ClientSecret = GetSetting(GetPropertyName(() => ClientSecret)); + FolderWithTcx = GetSetting(GetPropertyName(() => FolderWithTcx)); + + if (string.IsNullOrEmpty(ClientId) || string.IsNullOrEmpty(ClientSecret) || string.IsNullOrEmpty(FolderWithTcx)) + return false; + + return true; + } + + private static string GetSetting(string key) + { + return ConfigurationManager.AppSettings[key]; + } + + private static void SetSetting(string key, string value) + { + Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + configuration.AppSettings.Settings[key].Value = value; + configuration.Save(ConfigurationSaveMode.Full, true); + ConfigurationManager.RefreshSection("appSettings"); + } + + private static string GetPropertyName(Expression> expr) + { + var member = expr.Body as MemberExpression; + if (member == null) + throw new InvalidOperationException("Expression is not a member access expression."); + var property = member.Member as PropertyInfo; + if (property == null) + throw new InvalidOperationException("Member in expression is not a property."); + return property.Name; + } + + private static string ShowInputBox(string prompt, string title) + { + return Microsoft.VisualBasic.Interaction.InputBox(prompt, + title, + string.Empty, + 0, + 0); + } + } +} diff --git a/Strava/Properties/AssemblyInfo.cs b/Strava/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ebbe3d0 --- /dev/null +++ b/Strava/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Strava")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Strava")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1d30a9d5-4481-49ec-abec-c458ae41013b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Strava/Strava.csproj b/Strava/Strava.csproj new file mode 100644 index 0000000..5b27af7 --- /dev/null +++ b/Strava/Strava.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {1D30A9D5-4481-49EC-ABEC-C458AE41013B} + Exe + Strava + Strava + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Strava.NET.3.4.4\lib\net46\StravaDotNet.dll + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Strava/packages.config b/Strava/packages.config new file mode 100644 index 0000000..e9f3b7c --- /dev/null +++ b/Strava/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file