├── .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 |
--------------------------------------------------------------------------------