├── .gitignore ├── InstaSharp.sln ├── InstaSharp ├── InstaSharp.csproj ├── InstagramApi.cs ├── InstagramUploader.cs ├── Models │ ├── ErrorResponse.cs │ ├── LoggedInUserResponse.cs │ ├── NormalResponse.cs │ └── UploadResponse.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── Utilities │ ├── ImageUtilities.cs │ └── StringUtilities.cs ├── app.config └── packages.config ├── InstaSharpExample ├── App.config ├── InstaSharpExample.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | *_i.c 22 | *_p.c 23 | *.ilk 24 | *.meta 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | 41 | # Visual C++ cache files 42 | ipch/ 43 | *.aps 44 | *.ncb 45 | *.opensdf 46 | *.sdf 47 | 48 | # Visual Studio profiler 49 | *.psess 50 | *.vsp 51 | *.vspx 52 | 53 | # Guidance Automation Toolkit 54 | *.gpState 55 | 56 | # ReSharper is a .NET coding add-in 57 | _ReSharper* 58 | 59 | # NCrunch 60 | *.ncrunch* 61 | .*crunch*.local.xml 62 | 63 | # Installshield output folder 64 | [Ee]xpress 65 | 66 | # DocProject is a documentation generator add-in 67 | DocProject/buildhelp/ 68 | DocProject/Help/*.HxT 69 | DocProject/Help/*.HxC 70 | DocProject/Help/*.hhc 71 | DocProject/Help/*.hhk 72 | DocProject/Help/*.hhp 73 | DocProject/Help/Html2 74 | DocProject/Help/html 75 | 76 | # Click-Once directory 77 | publish 78 | 79 | # Publish Web Output 80 | *.Publish.xml 81 | 82 | # NuGet Packages Directory 83 | packages 84 | 85 | # Windows Azure Build Output 86 | csx 87 | *.build.csdef 88 | 89 | # Windows Store app package directory 90 | AppPackages/ 91 | 92 | # Others 93 | [Bb]in 94 | [Oo]bj 95 | sql 96 | TestResults 97 | [Tt]est[Rr]esult* 98 | *.Cache 99 | ClientBin 100 | [Ss]tyle[Cc]op.* 101 | ~$* 102 | *.dbmdl 103 | Generated_Code #added for RIA/Silverlight projects 104 | 105 | # Backup & report files from converting an old project file to a newer 106 | # Visual Studio version. Backup files are not needed, because we have git ;-) 107 | _UpgradeReport_Files/ 108 | Backup*/ 109 | UpgradeLog*.XML 110 | 111 | Installer/*setup.exe 112 | -------------------------------------------------------------------------------- /InstaSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstaSharp", "InstaSharp\InstaSharp.csproj", "{891D6A1C-DD9F-4186-98C0-2B1168BD71F5}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstaSharpExample", "InstaSharpExample\InstaSharpExample.csproj", "{F88AB7A3-C4D9-4277-9CA5-A8EB7C89CA3B}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {891D6A1C-DD9F-4186-98C0-2B1168BD71F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {891D6A1C-DD9F-4186-98C0-2B1168BD71F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {891D6A1C-DD9F-4186-98C0-2B1168BD71F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {891D6A1C-DD9F-4186-98C0-2B1168BD71F5}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {F88AB7A3-C4D9-4277-9CA5-A8EB7C89CA3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {F88AB7A3-C4D9-4277-9CA5-A8EB7C89CA3B}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {F88AB7A3-C4D9-4277-9CA5-A8EB7C89CA3B}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {F88AB7A3-C4D9-4277-9CA5-A8EB7C89CA3B}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /InstaSharp/InstaSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {891D6A1C-DD9F-4186-98C0-2B1168BD71F5} 8 | Library 9 | Properties 10 | InstaSharp 11 | InstaSharp 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll 35 | True 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | True 58 | True 59 | Settings.settings 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | PublicSettingsSingleFileGenerator 70 | Settings.Designer.cs 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /InstaSharp/InstagramApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | 8 | namespace InstaSharp 9 | { 10 | internal class InstagramApi 11 | { 12 | private const string ApiEndpoint = "https://i.instagram.com/api/v1/"; 13 | public CookieCollection CookieCollection; 14 | public HttpWebRequest WebRequest; 15 | public HttpWebResponse WebResponse; 16 | 17 | public string PostData(string endpoint, string postData, string userAgent) 18 | { 19 | string url = $"{ApiEndpoint}{endpoint}"; 20 | WebRequest = (HttpWebRequest) System.Net.WebRequest.Create(url); 21 | WebRequest.UserAgent = userAgent; 22 | 23 | WebRequest.CookieContainer = new CookieContainer(); 24 | //lets make sure we add our cookies back 25 | if (CookieCollection != null && CookieCollection.Count > 0) 26 | { 27 | WebRequest.CookieContainer.Add(CookieCollection); 28 | } 29 | WebRequest.Method = "POST"; 30 | WebRequest.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"; 31 | try 32 | { 33 | var postBytes = Encoding.UTF8.GetBytes(postData); 34 | WebRequest.ContentLength = postBytes.Length; 35 | var postDataStream = WebRequest.GetRequestStream(); 36 | postDataStream.Write(postBytes, 0, postBytes.Length); 37 | postDataStream.Close(); 38 | try 39 | { 40 | WebResponse = (HttpWebResponse) WebRequest.GetResponse(); 41 | //check if the status code is http 200 or http ok 42 | 43 | if (WebResponse.StatusCode == HttpStatusCode.OK) 44 | { 45 | //get all the cookies from the current request and add them to the response object cookies 46 | 47 | WebResponse.Cookies = WebRequest.CookieContainer.GetCookies(WebRequest.RequestUri); 48 | 49 | if (WebResponse.Cookies.Count > 0) 50 | { 51 | //check if this is the first request/response, if this is the response of first request cookieCollection 52 | //will be null 53 | if (CookieCollection == null) 54 | { 55 | CookieCollection = WebResponse.Cookies; 56 | } 57 | else 58 | { 59 | foreach (Cookie oRespCookie in WebResponse.Cookies) 60 | { 61 | var bMatch = false; 62 | foreach ( 63 | var oReqCookie in 64 | CookieCollection.Cast() 65 | .Where(oReqCookie => oReqCookie.Name == oRespCookie.Name)) 66 | { 67 | oReqCookie.Value = oRespCookie.Value; 68 | bMatch = true; 69 | break; 70 | } 71 | if (!bMatch) 72 | CookieCollection.Add(oRespCookie); 73 | } 74 | } 75 | } 76 | var reader = new StreamReader(WebResponse.GetResponseStream()); 77 | var responseString = reader.ReadToEnd(); 78 | reader.Close(); 79 | return responseString; 80 | } 81 | } 82 | catch (WebException wex) 83 | { 84 | if (wex.Response != null) 85 | { 86 | using (var errorResponse = (HttpWebResponse) wex.Response) 87 | { 88 | using (var reader = new StreamReader(errorResponse.GetResponseStream())) 89 | { 90 | var error = reader.ReadToEnd(); 91 | return error; 92 | } 93 | } 94 | } 95 | } 96 | } 97 | catch (Exception ex) 98 | { 99 | Console.WriteLine(ex.Message); 100 | } 101 | return "Error in posting data"; 102 | } 103 | 104 | 105 | public string PostImage(string localImagePath, string userAgent) 106 | { 107 | var url = $"{ApiEndpoint}media/upload/"; 108 | const string paramName = "photo"; 109 | const string contentType = "image/jpeg"; 110 | var nvc = new NameValueCollection 111 | { 112 | {"device_timestamp", (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds + ""} 113 | }; 114 | var responseStr = string.Empty; 115 | 116 | try 117 | { 118 | var boundary = $"---------------------------{DateTime.Now.Ticks}"; 119 | var boundarybytes = Encoding.ASCII.GetBytes($"\r\n--{boundary}\r\n"); 120 | WebRequest = (HttpWebRequest) System.Net.WebRequest.Create(url); 121 | WebRequest.ContentType = $"multipart/form-data; boundary={boundary}"; 122 | WebRequest.Method = "POST"; 123 | WebRequest.KeepAlive = true; 124 | WebRequest.Credentials = CredentialCache.DefaultCredentials; 125 | WebRequest.UserAgent = userAgent; 126 | 127 | WebRequest.CookieContainer = new CookieContainer(); 128 | if (CookieCollection != null && CookieCollection.Count > 0) 129 | { 130 | WebRequest.CookieContainer.Add(CookieCollection); 131 | } 132 | 133 | using (var requestStream = WebRequest.GetRequestStream()) 134 | { 135 | var contentTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; 136 | foreach (string key in nvc.Keys) 137 | { 138 | requestStream.Write(boundarybytes, 0, boundarybytes.Length); 139 | var formitem = string.Format(contentTemplate, key, nvc[key]); 140 | var formitembytes = Encoding.UTF8.GetBytes(formitem); 141 | requestStream.Write(formitembytes, 0, formitembytes.Length); 142 | } 143 | requestStream.Write(boundarybytes, 0, boundarybytes.Length); 144 | var fileName = Path.GetFileName(localImagePath); 145 | 146 | var headerTemplate = 147 | "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; 148 | var header = string.Format(headerTemplate, paramName, fileName, contentType); 149 | var headerbytes = Encoding.UTF8.GetBytes(header); 150 | requestStream.Write(headerbytes, 0, headerbytes.Length); 151 | 152 | using (var fileStream = new FileStream(localImagePath, FileMode.Open, FileAccess.Read)) 153 | { 154 | var buffer = new byte[4096]; 155 | var bytesRead = 0; 156 | while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) 157 | { 158 | requestStream.Write(buffer, 0, bytesRead); 159 | } 160 | } 161 | var trailer = Encoding.ASCII.GetBytes($"\r\n--{boundary}--\r\n"); 162 | requestStream.Write(trailer, 0, trailer.Length); 163 | } 164 | 165 | if (CookieCollection != null && CookieCollection.Count > 0) 166 | { 167 | WebRequest.CookieContainer.Add(CookieCollection); 168 | } 169 | 170 | WebResponse wresp = null; 171 | try 172 | { 173 | wresp = WebRequest.GetResponse(); 174 | var downStream = wresp.GetResponseStream(); 175 | if (downStream != null) 176 | { 177 | using (var downReader = new StreamReader(downStream)) 178 | { 179 | responseStr = downReader.ReadToEnd(); 180 | } 181 | } 182 | return responseStr; 183 | } 184 | catch (Exception ex) 185 | { 186 | Console.WriteLine(ex.Message); 187 | if (wresp != null) 188 | { 189 | wresp.Close(); 190 | wresp = null; 191 | } 192 | } 193 | finally 194 | { 195 | WebRequest = null; 196 | } 197 | return responseStr; 198 | } 199 | catch (Exception ex) 200 | { 201 | Console.WriteLine(ex.Message); 202 | // ignored 203 | } 204 | return responseStr; 205 | } 206 | } 207 | } -------------------------------------------------------------------------------- /InstaSharp/InstagramUploader.cs: -------------------------------------------------------------------------------- 1 | #region 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Drawing; 6 | using System.Drawing.Imaging; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Security; 10 | using System.Security.Cryptography; 11 | using System.Text; 12 | using System.Text.RegularExpressions; 13 | using System.Web; 14 | using System.Web.Script.Serialization; 15 | using InstaSharp.Models; 16 | using InstaSharp.Properties; 17 | using InstaSharp.Utilities; 18 | using Newtonsoft.Json.Linq; 19 | 20 | #endregion 21 | 22 | namespace InstaSharp 23 | { 24 | public class InstagramUploader 25 | { 26 | private readonly SecureString _password; 27 | 28 | private readonly string _userAgent = 29 | "Instagram 6.21.2 Android (19/4.4.2; 480dpi; 1152x1920; Meizu; MX4; mx4; mt6595; en_US)"; 30 | 31 | private readonly string _username; 32 | 33 | 34 | private readonly string instagramSignature = "25eace5393646842f0d0c3fb2ac7d3cfa15c052436ee86b5406a8433f54d24a5"; 35 | private readonly JavaScriptSerializer _serializer = new JavaScriptSerializer(); 36 | 37 | 38 | public InstagramUploader(string username, SecureString password) 39 | { 40 | _username = username; 41 | _password = password; 42 | } 43 | 44 | public event EventHandler OnLoginEvent; 45 | public event EventHandler InvalidLoginEvent; 46 | public event EventHandler SuccessfulLoginEvent; 47 | public event EventHandler ErrorEvent; 48 | public event EventHandler OnMediaUploadStartedEvent; 49 | public event EventHandler OnMediaUploadeComplete; 50 | public event EventHandler OnMediaConfigureStarted; 51 | public event EventHandler OnCompleteEvent; 52 | 53 | private string GenerateGuid() 54 | { 55 | return Guid.NewGuid().ToString(); 56 | } 57 | 58 | private string GenerateSignature(string data) 59 | { 60 | var keyByte = Encoding.UTF8.GetBytes(instagramSignature); 61 | using (var hmacsha256 = new HMACSHA256(keyByte)) 62 | { 63 | hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(data)); 64 | return hmacsha256.Hash.Aggregate("", (current, t) => current + t.ToString("X2")).ToLower(); 65 | } 66 | } 67 | 68 | private string Crop(string imagePath, bool withBorder = false) 69 | { 70 | var tempImage = Path.Combine(Path.GetTempPath(), StringUtilities.GetRandomString() + ".jpg"); 71 | var primaryImage = new Bitmap(Image.FromFile(imagePath)); 72 | if (withBorder) 73 | { 74 | var croppedBorder = ImageUtilities.SquareWithBorder(primaryImage); 75 | croppedBorder.Save(tempImage, ImageFormat.Jpeg); 76 | } 77 | else 78 | { 79 | var squaredImage = ImageUtilities.SquareImage(primaryImage); 80 | squaredImage.Save(tempImage, ImageFormat.Jpeg); 81 | } 82 | return tempImage; 83 | } 84 | 85 | public void UploadImage(string imagePath, string caption, bool crop = false, bool withBorder = false) 86 | { 87 | var instagramApi = new InstagramApi(); 88 | if (crop) 89 | { 90 | imagePath = Crop(imagePath, withBorder); 91 | } 92 | try 93 | { 94 | string guid; 95 | string deviceId; 96 | //save our device/guid so we look like we're always uploading from the same phone 97 | if (!string.IsNullOrEmpty(Settings.Default.deviceId)) 98 | { 99 | guid = Settings.Default.guid; 100 | deviceId = Settings.Default.deviceId; 101 | } 102 | else 103 | { 104 | guid = GenerateGuid(); 105 | deviceId = $"android-{guid}"; 106 | Settings.Default.deviceId = deviceId; 107 | Settings.Default.guid = guid; 108 | Settings.Default.Save(); 109 | } 110 | var data = new Dictionary 111 | { 112 | {"device_id", deviceId}, 113 | {"guid", guid}, 114 | {"username", _username}, 115 | {"password", StringUtilities.SecureStringToString(_password)}, 116 | {"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"} 117 | }; 118 | var loginData = _serializer.Serialize(data); 119 | var signature = GenerateSignature(loginData); 120 | var signedLoginData = 121 | $"signed_body={signature}.{HttpUtility.UrlEncode(loginData)}&ig_sig_key_version=6"; 122 | OnLoginEvent?.Invoke(this, new NormalResponse {Status = "ok", Message = "Logging in please wait."}); 123 | var loginResponse = instagramApi.PostData("accounts/login/", signedLoginData, _userAgent); 124 | if (string.IsNullOrEmpty(loginResponse)) 125 | { 126 | ErrorEvent?.Invoke(this, 127 | new ErrorResponse 128 | { 129 | Status = "fail", 130 | Message = "Empty response received from the server while trying to login" 131 | }); 132 | return; 133 | } 134 | try 135 | { 136 | var loginJson = JObject.Parse(loginResponse); 137 | var status = (string) loginJson["status"]; 138 | if (status.Equals("ok")) 139 | { 140 | var username = (string) loginJson["logged_in_user"]["username"]; 141 | var hasAnonymousProfilePicture = 142 | (bool) loginJson["logged_in_user"]["has_anonymous_profile_picture"]; 143 | var profilePicUrl = (string) loginJson["logged_in_user"]["profile_pic_url"]; 144 | var fullName = (string) loginJson["logged_in_user"]["full_name"]; 145 | var isPrivate = (bool) loginJson["logged_in_user"]["is_private"]; 146 | SuccessfulLoginEvent?.Invoke(this, 147 | new LoggedInUserResponse 148 | { 149 | Username = username, 150 | HasAnonymousProfilePicture = hasAnonymousProfilePicture, 151 | ProfilePicUrl = profilePicUrl, 152 | FullName = fullName, 153 | IsPrivate = isPrivate 154 | }); 155 | OnMediaUploadStartedEvent?.Invoke(this, EventArgs.Empty); 156 | var uploadResponse = instagramApi.PostImage(imagePath, _userAgent); 157 | if (string.IsNullOrEmpty(uploadResponse)) 158 | { 159 | ErrorEvent?.Invoke(this, 160 | new ErrorResponse 161 | { 162 | Status = "fail", 163 | Message = 164 | "Empty response received from the server while trying to post the image" 165 | }); 166 | return; 167 | } 168 | try 169 | { 170 | var uploadJson = JObject.Parse(uploadResponse); 171 | var uploadStatus = (string) uploadJson["status"]; 172 | if (uploadStatus.Equals("ok")) 173 | { 174 | OnMediaUploadeComplete?.Invoke(this, EventArgs.Empty); 175 | OnMediaConfigureStarted?.Invoke(this, EventArgs.Empty); 176 | var newLineStripper = new Regex(@"/\r|\n/", RegexOptions.IgnoreCase); 177 | //... 178 | caption = newLineStripper.Replace(caption, ""); 179 | var mediaId = (string) uploadJson["media_id"]; 180 | var configureData = new Dictionary 181 | { 182 | {"device_id", deviceId}, 183 | {"guid", guid}, 184 | {"media_id", mediaId}, 185 | {"caption", caption.Trim()}, 186 | {"device_timestamp", StringUtilities.GenerateTimeStamp()}, 187 | {"source_type", "5"}, 188 | {"filter_type", "0"}, 189 | {"extra", "{}"}, 190 | {"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"} 191 | }; 192 | var configureDataString = _serializer.Serialize(configureData); 193 | var configureSignature = GenerateSignature(configureDataString); 194 | var signedConfigureBody = 195 | $"signed_body={configureSignature}.{HttpUtility.UrlEncode(configureDataString)}&ig_sig_key_version=4"; 196 | var configureResults = instagramApi.PostData("media/configure/", 197 | signedConfigureBody, _userAgent); 198 | if (string.IsNullOrEmpty(configureResults)) 199 | { 200 | ErrorEvent?.Invoke(this, 201 | new ErrorResponse 202 | { 203 | Status = "fail", 204 | Message = 205 | "Empty response received from the server while trying to configure the image" 206 | }); 207 | return; 208 | } 209 | 210 | try 211 | { 212 | var configureJson = JObject.Parse(configureResults); 213 | var configureStatus = (string) configureJson["status"]; 214 | if (configureStatus.Equals("fail")) 215 | { 216 | ErrorEvent?.Invoke(this, 217 | new ErrorResponse 218 | { 219 | Status = "fail", 220 | Message = (string) configureJson["message"] 221 | }); 222 | return; 223 | } 224 | 225 | var uploadedResponse = new UploadResponse 226 | { 227 | Images = new List() 228 | }; 229 | foreach ( 230 | var media in 231 | configureJson["media"]["image_versions2"]["candidates"].Select( 232 | x => JObject.Parse(x.ToString())) 233 | .Select(mediaJson => new UploadResponse.InstagramMedia 234 | { 235 | Url = (string) mediaJson["url"], 236 | Width = (int) mediaJson["width"], 237 | Height = (int) mediaJson["height"] 238 | })) 239 | { 240 | uploadedResponse.Images.Add(media); 241 | } 242 | OnCompleteEvent?.Invoke(this, uploadedResponse); 243 | } 244 | 245 | catch (Exception ex) 246 | { 247 | ErrorEvent?.Invoke(this, 248 | new ErrorResponse 249 | { 250 | Status = "fail", 251 | Message = "Could not decode the configure response" 252 | }); 253 | } 254 | } 255 | 256 | else 257 | { 258 | ErrorEvent?.Invoke(this, 259 | new ErrorResponse 260 | { 261 | Status = "fail", 262 | Message = 263 | (string) uploadJson["message"] 264 | }); 265 | } 266 | } 267 | catch (Exception) 268 | { 269 | ErrorEvent?.Invoke(this, 270 | new ErrorResponse 271 | { 272 | Status = "fail", 273 | Message = "Could not decode the upload response" 274 | }); 275 | } 276 | } 277 | 278 | else 279 | { 280 | var message = (string) loginJson["message"]; 281 | InvalidLoginEvent?.Invoke(this, new ErrorResponse {Status = status, Message = message}); 282 | } 283 | } 284 | catch (Exception) 285 | { 286 | ErrorEvent?.Invoke(this, 287 | new ErrorResponse 288 | { 289 | Status = "fail", 290 | Message = "Could not decode the login response" 291 | }); 292 | } 293 | } 294 | finally 295 | { 296 | //clean up 297 | if (crop) 298 | { 299 | if (File.Exists(imagePath)) 300 | { 301 | File.Delete(imagePath); 302 | } 303 | } 304 | } 305 | } 306 | } 307 | } -------------------------------------------------------------------------------- /InstaSharp/Models/ErrorResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InstaSharp.Models 4 | { 5 | public class ErrorResponse : EventArgs 6 | { 7 | public string Status { get; set; } 8 | public string Message { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /InstaSharp/Models/LoggedInUserResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InstaSharp.Models 4 | { 5 | public class LoggedInUserResponse : EventArgs 6 | { 7 | public string Username { get; set; } 8 | public bool HasAnonymousProfilePicture { get; set; } 9 | public string ProfilePicUrl { get; set; } 10 | public string FullName { get; set; } 11 | public long Pk { get; set; } 12 | public bool IsPrivate { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /InstaSharp/Models/NormalResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InstaSharp.Models 4 | { 5 | public class NormalResponse : EventArgs 6 | { 7 | public string Status { get; set; } 8 | public string Message { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /InstaSharp/Models/UploadResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace InstaSharp.Models 5 | { 6 | public class UploadResponse : EventArgs 7 | { 8 | public List Images { get; set; } 9 | 10 | public class InstagramMedia 11 | { 12 | public string Url { get; set; } 13 | public int Width { get; set; } 14 | public int Height { get; set; } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /InstaSharp/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 | 8 | [assembly: AssemblyTitle("InstaSharp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("InstaSharp")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 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 | 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | 25 | [assembly: Guid("891d6a1c-dd9f-4186-98c0-2b1168bd71f5")] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [assembly: AssemblyVersion("1.0.*")] 37 | 38 | [assembly: AssemblyVersion("1.0.0.0")] 39 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /InstaSharp/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace InstaSharp.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string deviceId { 30 | get { 31 | return ((string)(this["deviceId"])); 32 | } 33 | set { 34 | this["deviceId"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("")] 41 | public string guid { 42 | get { 43 | return ((string)(this["guid"])); 44 | } 45 | set { 46 | this["guid"] = value; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /InstaSharp/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /InstaSharp/Utilities/ImageUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Drawing2D; 4 | 5 | namespace InstaSharp.Utilities 6 | { 7 | public class ImageUtilities 8 | { 9 | private static Bitmap CropImage(Bitmap original, Rectangle cropArea) 10 | { 11 | return original.Clone(cropArea, original.PixelFormat); 12 | } 13 | 14 | public static Bitmap SquareWithBorder(Bitmap original, Brush fillBrush = null) 15 | { 16 | var largestDimension = Math.Max(original.Height, original.Width); 17 | var squareSize = new Size(largestDimension, largestDimension); 18 | var squareImage = new Bitmap(squareSize.Width, squareSize.Height); 19 | using (var graphics = Graphics.FromImage(squareImage)) 20 | { 21 | graphics.FillRectangle(fillBrush ?? Brushes.White, 0, 0, squareSize.Width, squareSize.Height); 22 | graphics.CompositingQuality = CompositingQuality.HighQuality; 23 | graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 24 | graphics.SmoothingMode = SmoothingMode.HighQuality; 25 | 26 | graphics.DrawImage(original, squareSize.Width/2 - original.Width/2, 27 | squareSize.Height/2 - original.Height/2, original.Width, original.Height); 28 | } 29 | return squareImage; 30 | } 31 | 32 | public static Bitmap SquareImage(Bitmap original) 33 | { 34 | var width = original.Width; 35 | var height = original.Height; 36 | var cropArea = new Rectangle(); 37 | if (width > height) 38 | { 39 | cropArea.Width = height; 40 | cropArea.Height = height; 41 | cropArea.X = 0; 42 | cropArea.Y = 0; 43 | } 44 | else if (width < height) 45 | { 46 | cropArea.Width = width; 47 | cropArea.Height = width; 48 | cropArea.X = 0; 49 | cropArea.Y = 0; 50 | } 51 | Bitmap croppedImage = null; 52 | if (width != height) croppedImage = CropImage(original, cropArea); 53 | return croppedImage; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /InstaSharp/Utilities/StringUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Security; 5 | 6 | namespace InstaSharp.Utilities 7 | { 8 | public class StringUtilities 9 | { 10 | public static string GetRandomString() 11 | { 12 | var path = Path.GetRandomFileName(); 13 | path = path.Replace(".", ""); // Remove period. 14 | return path; 15 | } 16 | 17 | public static string GenerateTimeStamp() 18 | { 19 | var strGenerateTimeStamp = string.Empty; 20 | try 21 | { 22 | // Default implementation of UNIX time of the current UTC time 23 | var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 24 | strGenerateTimeStamp = Convert.ToInt64(ts.TotalSeconds).ToString(); 25 | } 26 | catch (Exception ex) 27 | { 28 | // ignored 29 | } 30 | return strGenerateTimeStamp; 31 | } 32 | 33 | public static string SecureStringToString(SecureString value) 34 | { 35 | var valuePtr = IntPtr.Zero; 36 | try 37 | { 38 | valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value); 39 | return Marshal.PtrToStringUni(valuePtr); 40 | } 41 | finally 42 | { 43 | Marshal.ZeroFreeGlobalAllocUnicode(valuePtr); 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /InstaSharp/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /InstaSharp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /InstaSharpExample/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /InstaSharpExample/InstaSharpExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F88AB7A3-C4D9-4277-9CA5-A8EB7C89CA3B} 8 | Exe 9 | Properties 10 | InstaSharpExample 11 | InstaSharpExample 12 | v4.5.2 13 | 512 14 | true 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 | ..\InstaSharp\bin\Debug\InstaSharp.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /InstaSharpExample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security; 3 | using InstaSharp; 4 | using InstaSharp.Models; 5 | 6 | namespace InstaSharpExample 7 | { 8 | internal class Program 9 | { 10 | public static SecureString ConvertToSecureString(string strPassword) 11 | { 12 | var secureStr = new SecureString(); 13 | if (strPassword.Length > 0) 14 | { 15 | foreach (var c in strPassword.ToCharArray()) secureStr.AppendChar(c); 16 | } 17 | return secureStr; 18 | } 19 | 20 | private static void Main(string[] args) 21 | { 22 | var uploader = new InstagramUploader("", ConvertToSecureString("")); 23 | uploader.InvalidLoginEvent += InvalidLoginEvent; 24 | uploader.ErrorEvent += ErrorEvent; 25 | uploader.OnCompleteEvent += OnCompleteEvent; 26 | uploader.OnLoginEvent += OnLoginEvent; 27 | uploader.SuccessfulLoginEvent += SuccessfulLoginEvent; 28 | uploader.OnMediaConfigureStarted += OnMediaConfigureStarted; 29 | uploader.OnMediaUploadStartedEvent += OnMediaUploadStartedEvent; 30 | uploader.OnMediaUploadeComplete += OnmediaUploadCompleteEvent; 31 | uploader.UploadImage(@"", "#helloworld"); 32 | Console.WriteLine("Your DeviceID is " + InstaSharp.Properties.Settings.Default.deviceId); 33 | Console.Read(); 34 | } 35 | 36 | private static void OnMediaUploadStartedEvent(object sender, EventArgs e) 37 | { 38 | Console.WriteLine("Attempting to upload image"); 39 | } 40 | 41 | private static void OnmediaUploadCompleteEvent(object sender, EventArgs e) 42 | { 43 | Console.WriteLine("The image was uploaded, but has not been configured yet."); 44 | } 45 | 46 | 47 | private static void OnMediaConfigureStarted(object sender, EventArgs e) 48 | { 49 | Console.WriteLine("The image has started to be configured"); 50 | } 51 | 52 | private static void SuccessfulLoginEvent(object sender, EventArgs e) 53 | { 54 | Console.WriteLine("Logged in! " + ((LoggedInUserResponse) e).FullName); 55 | } 56 | 57 | private static void OnLoginEvent(object sender, EventArgs e) 58 | { 59 | Console.WriteLine("Event fired for login: " + ((NormalResponse) e).Message); 60 | } 61 | 62 | private static void OnCompleteEvent(object sender, EventArgs e) 63 | { 64 | Console.WriteLine("Image posted to Instagram, here are all the urls"); 65 | foreach (var image in ((UploadResponse) e).Images) 66 | { 67 | Console.WriteLine("Url: " + image.Url); 68 | Console.WriteLine("Width: " + image.Width); 69 | Console.WriteLine("Height: " + image.Height); 70 | } 71 | } 72 | 73 | private static void ErrorEvent(object sender, EventArgs e) 74 | { 75 | Console.WriteLine("Error " + ((ErrorResponse) e).Message); 76 | } 77 | 78 | private static void InvalidLoginEvent(object sender, EventArgs e) 79 | { 80 | Console.WriteLine("Error while logging " + ((ErrorResponse) e).Message); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /InstaSharpExample/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("InstaSharpExample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("InstaSharpExample")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 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("f88ab7a3-c4d9-4277-9ca5-a8eb7c89ca3b")] 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## InstaSharp 2 | 3 | 4 | ### What is it? 5 | This library allows you to upload photos to Instagram without a mobile device, thus allowing you to extend your application to support Instagram uploading. It is multiplatform and works on Windows XP - 10, UWP and Windows 8 phones. 6 | 7 | ### Usage 8 | 9 | Create an instance and define your events 10 | ```csharp 11 | var uploader = new InstagramUploader("username", ConvertToSecureString("password")); 12 | uploader.InvalidLoginEvent += InvalidLoginEvent; 13 | uploader.ErrorEvent += ErrorEvent; 14 | uploader.OnCompleteEvent += OnCompleteEvent; 15 | uploader.OnLoginEvent += OnLoginEvent; 16 | uploader.SuccessfulLoginEvent += SuccessfulLoginEvent; 17 | uploader.OnMediaConfigureStarted += OnMediaConfigureStarted; 18 | uploader.OnMediaUploadStartedEvent += OnMediaUploadStartedEvent; 19 | uploader.OnMediaUploadeComplete += OnmediaUploadCompleteEvent; 20 | ``` 21 | 22 | To upload a photo call the upload function 23 | ```csharp 24 | uploader.UploadImage(@"fullPathToImage", "Caption and Hashtags", cropImage, cropWithBorder); 25 | ``` 26 | 27 | cropImage and cropWithBorder are optional, cropping an image will turn it into a perfect square, cropping with a border will maintain the images aspect ratio by putting a border around the image. If an image fails to upload, it may be because its dimensions are incorrect for Instagram and you might need to crop. 28 | 29 | 30 | All calls are made over HTTPs to Instagram. 31 | 32 | You can handle all your events using the following code 33 | 34 | ```csharp 35 | private static void OnMediaUploadStartedEvent(object sender, EventArgs e) 36 | { 37 | Console.WriteLine("Attempting to upload image"); 38 | } 39 | 40 | private static void OnmediaUploadCompleteEvent(object sender, EventArgs e) 41 | { 42 | Console.WriteLine("The image was uploaded, but has not been configured yet."); 43 | } 44 | 45 | 46 | private static void OnMediaConfigureStarted(object sender, EventArgs e) 47 | { 48 | Console.WriteLine("The image has started to be configured"); 49 | } 50 | 51 | private static void SuccessfulLoginEvent(object sender, EventArgs e) 52 | { 53 | Console.WriteLine("Logged in! " + ((LoggedInUserResponse) e).FullName); 54 | } 55 | 56 | private static void OnLoginEvent(object sender, EventArgs e) 57 | { 58 | Console.WriteLine("Event fired for login: " + ((NormalResponse) e).Message); 59 | } 60 | 61 | private static void OnCompleteEvent(object sender, EventArgs e) 62 | { 63 | Console.WriteLine("Image posted to Instagram, here are all the urls"); 64 | foreach (var image in ((UploadResponse) e).Images) 65 | { 66 | Console.WriteLine("Url: " + image.Url); 67 | Console.WriteLine("Width: " + image.Width); 68 | Console.WriteLine("Height: " + image.Height); 69 | } 70 | } 71 | 72 | private static void ErrorEvent(object sender, EventArgs e) 73 | { 74 | Console.WriteLine("Error " + ((ErrorResponse) e).Message); 75 | } 76 | 77 | private static void InvalidLoginEvent(object sender, EventArgs e) 78 | { 79 | Console.WriteLine("Error while logging " + ((ErrorResponse) e).Message); 80 | } 81 | ``` 82 | 83 | 84 | ### Contributing 85 | 86 | I accept pull request, feel free to submit them. 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | --------------------------------------------------------------------------------