PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Code/Channels/LinkedIn/LinkedInClientChannel.cs

http://github.com/waseems/inbox2_desktop
C# | 342 lines | 271 code | 53 blank | 18 comment | 15 complexity | 087816da81bd479a3881d41104a89c8c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Xml.Linq;
  6. using Inbox2.Platform.Channels.Entities;
  7. using Inbox2.Platform.Channels.Interfaces;
  8. using Inbox2.Platform.Channels.Extensions;
  9. namespace Inbox2.Channels.LinkedIn
  10. {
  11. public class LinkedInClientChannel : IClientInputChannel, IClientContactsChannel, IClientStatusUpdatesChannel
  12. {
  13. public string Hostname
  14. {
  15. get { return "http://m.linkedin.com"; }
  16. set { }
  17. }
  18. public int Port
  19. {
  20. get { return 80; }
  21. set { }
  22. }
  23. public bool IsSecured
  24. {
  25. get { return false; }
  26. set { }
  27. }
  28. public bool IsEnabled { get; set; }
  29. public int MaxConcurrentConnections { get; set; }
  30. public IChannelCredentialsProvider CredentialsProvider { get; set; }
  31. public string Protocol
  32. {
  33. get { return "LinkedIn"; }
  34. }
  35. /// <summary>
  36. /// See: http://developer.linkedin.com/docs/DOC-1004
  37. /// </summary>
  38. /// <returns></returns>
  39. public IEnumerable<ChannelContact> GetContacts()
  40. {
  41. var result = LinkedInWebRequest.PerformRequest(new Uri("http://api.linkedin.com/v1/people/~/connections"),
  42. ChannelHelper.ConsumerKey, ChannelHelper.ConsumerSecret, ChannelHelper.Token, ChannelHelper.TokenSecret);
  43. if (result.Element("connections").Elements().Count() == 0)
  44. yield break;
  45. foreach (var response in result.Elements("connections").Elements("person"))
  46. {
  47. ChannelContact contact = new ChannelContact();
  48. contact.Profile.ChannelProfileKey = response.Element("id").Value;
  49. contact.Person.Firstname = response.Element("first-name").Value;
  50. contact.Person.Lastname = response.Element("last-name").Value;
  51. contact.Profile.Title = response.Element("headline").Value;
  52. contact.Profile.Url = response.Element("site-standard-profile-request").Element("url").Value;
  53. if (response.Element("picture-url") != null)
  54. {
  55. contact.Profile.ChannelAvatar = new ChannelAvatar();
  56. contact.Profile.ChannelAvatar.Url = response.Element("picture-url").Value;
  57. }
  58. contact.Profile.SourceAddress = new SourceAddress(response.Element("id").Value, contact.Person.Name);
  59. contact.Profile.ProfileType = ProfileType.Social;
  60. yield return contact;
  61. }
  62. }
  63. IClientContactsChannel IClientContactsChannel.Clone()
  64. {
  65. return new LinkedInClientChannel
  66. {
  67. Hostname = Hostname,
  68. Port = Port,
  69. IsSecured = IsSecured,
  70. MaxConcurrentConnections = MaxConcurrentConnections,
  71. CredentialsProvider = CredentialsProvider
  72. };
  73. }
  74. public ChannelSocialProfile GetProfile()
  75. {
  76. var result = LinkedInWebRequest.PerformRequest(new Uri("http://api.linkedin.com/v1/people/~:(id,first-name,last-name,picture-url)"),
  77. ChannelHelper.ConsumerKey, ChannelHelper.ConsumerSecret, ChannelHelper.Token, ChannelHelper.TokenSecret);
  78. var response = result.Element("person");
  79. return new ChannelSocialProfile
  80. {
  81. Id = response.Element("id").Value,
  82. FullName = String.Concat(response.Element("first-name").Value, " ", response.Element("last-name").Value),
  83. AvatarUrl = response.Element("picture-url") == null ? String.Empty : response.Element("picture-url").Value
  84. };
  85. }
  86. public IEnumerable<ChannelStatusUpdate> GetMentions(int pageSize)
  87. {
  88. yield break;
  89. }
  90. public IEnumerable<ChannelStatusUpdate> GetUpdates(int pageSize)
  91. {
  92. var result = LinkedInWebRequest.PerformRequest(new Uri("http://api.linkedin.com/v1/people/~/network"), ChannelHelper.ConsumerKey, ChannelHelper.ConsumerSecret, ChannelHelper.Token, ChannelHelper.TokenSecret);
  93. if (result.Element("network").Elements().Count() == 0)
  94. yield break;
  95. foreach (var response in result.Element("network").Element("updates").Elements("update"))
  96. {
  97. ChannelStatusUpdate statusUpdate = null;
  98. var updateType = (LinkedInUpdateType)Enum.Parse(typeof(LinkedInUpdateType),
  99. response.Element("update-type").Value);
  100. switch (updateType)
  101. {
  102. case LinkedInUpdateType.ANSW:
  103. // TODO : Answer Update
  104. break;
  105. case LinkedInUpdateType.APPS:
  106. case LinkedInUpdateType.APPM:
  107. // TODO : Application Update
  108. break;
  109. case LinkedInUpdateType.CONN:
  110. case LinkedInUpdateType.NCON:
  111. case LinkedInUpdateType.CCEM:
  112. // Connection updates
  113. statusUpdate = new ChannelStatusUpdate
  114. {
  115. ChannelStatusKey = BuildChannelStatusKey(response),
  116. From = BuildSourceAddress(response),
  117. Status = BuildConnectionStatusMessage(response),
  118. DatePosted = GetDateTime(response)
  119. };
  120. break;
  121. case LinkedInUpdateType.JOBS:
  122. case LinkedInUpdateType.JOBP:
  123. // TODO : Posted a job
  124. break;
  125. case LinkedInUpdateType.JGRP:
  126. // TODO : Joined a group
  127. break;
  128. case LinkedInUpdateType.PICT:
  129. case LinkedInUpdateType.PICU:
  130. // TODO : Changed a picture
  131. break;
  132. case LinkedInUpdateType.RECU:
  133. case LinkedInUpdateType.PREC:
  134. // TODO : Recommendations
  135. break;
  136. case LinkedInUpdateType.PRFU:
  137. case LinkedInUpdateType.PROF:
  138. // TODO : Changed profile
  139. break;
  140. case LinkedInUpdateType.QSTN:
  141. // TODO : Question update
  142. break;
  143. case LinkedInUpdateType.STAT:
  144. // Status update
  145. statusUpdate = new ChannelStatusUpdate
  146. {
  147. ChannelStatusKey = BuildChannelStatusKey(response),
  148. From = BuildSourceAddress(response),
  149. Status = response.Element("update-content").Element("person").Element("current-status").Value,
  150. DatePosted = GetDateTime(response)
  151. };
  152. break;
  153. }
  154. if (statusUpdate != null)
  155. yield return statusUpdate;
  156. }
  157. }
  158. private DateTime GetDateTime(XElement response)
  159. {
  160. // LinkedIn returns us Milliseconds instead of seconds.
  161. // We first need to convert this to seconds.
  162. return ((long)TimeSpan.FromMilliseconds(Int64.Parse(response.Element("timestamp").Value)).TotalSeconds).ToUnixTime();
  163. }
  164. private string BuildChannelStatusKey(XElement response)
  165. {
  166. return string.Format(
  167. "{0}-{1}",
  168. response.Element("timestamp").Value,
  169. response.Element("update-content").Element("person").Element("id").Value
  170. );
  171. }
  172. private SourceAddress BuildSourceAddress(XElement response)
  173. {
  174. if (response.Element("update-content").Element("person").Element("picture-url") != null)
  175. {
  176. // User has avatar
  177. return new SourceAddress(
  178. response.Element("update-content").Element("person").Element("id").Value,
  179. string.Format("{0} {1}",
  180. response.Element("update-content").Element("person").Element("first-name").Value,
  181. response.Element("update-content").Element("person").Element("last-name").Value),
  182. response.Element("update-content").Element("person").Element("picture-url").Value,
  183. response.Element("update-content").Element("person").Element("site-standard-profile-request").Element("url").Value);
  184. }
  185. else
  186. {
  187. // No Avatar
  188. var address = new SourceAddress(
  189. response.Element("update-content").Element("person").Element("id").Value,
  190. string.Format("{0} {1}",
  191. response.Element("update-content").Element("person").Element("first-name").Value,
  192. response.Element("update-content").Element("person").Element("last-name").Value));
  193. address.ProfileUrl =
  194. response.Element("update-content").Element("person").Element("site-standard-profile-request").Element("url").Value;
  195. return address;
  196. }
  197. }
  198. private string BuildConnectionStatusMessage(XElement response)
  199. {
  200. StringBuilder stringBuilder = new StringBuilder();
  201. stringBuilder.Append("is now connected to ");
  202. var persons = response.Element("update-content").Element("person").Element("connections").Elements("person").ToArray();
  203. var connectedTo = new string[persons.Count()];
  204. for (int i = 0; i < persons.Count(); i++)
  205. {
  206. connectedTo[i] = String.Format("{0} {1}", persons[i].Element("first-name").Value, persons[i].Element("last-name").Value);
  207. }
  208. stringBuilder.Append(String.Join(", ", connectedTo));
  209. return stringBuilder.ToString();
  210. }
  211. public IEnumerable<ChannelStatusUpdate> GetUserUpdates(string username, int pageSize)
  212. {
  213. var result = LinkedInWebRequest.PerformRequest(new Uri(String.Concat("http://api.linkedin.com/v1/people/id=", username, ":(current-status,current-status-timestamp)")),
  214. ChannelHelper.ConsumerKey, ChannelHelper.ConsumerSecret, ChannelHelper.Token, ChannelHelper.TokenSecret);
  215. if (result.Elements("person").Elements().Count() == 0)
  216. yield break;
  217. foreach (var response in result.Elements("person"))
  218. {
  219. var update = new ChannelStatusUpdate();
  220. update.Status = response.Element("current-status").Value;
  221. var ts = TimeSpan.FromMilliseconds(Double.Parse(response.Element("current-status-timestamp").Value));
  222. update.DatePosted = ((long) ts.TotalSeconds).ToUnixTime();
  223. yield return update;
  224. }
  225. }
  226. public IEnumerable<ChannelStatusUpdate> GetUpdates(string keyword, int pageSize)
  227. {
  228. yield break;
  229. }
  230. public void UpdateMyStatus(ChannelStatusUpdate update)
  231. {
  232. var element = new XElement("current-status", update.Status.StripHtml());
  233. LinkedInWebRequest.Put(new Uri("http://api.linkedin.com/v1/people/~/current-status"), element.ToString(),
  234. ChannelHelper.ConsumerKey, ChannelHelper.ConsumerSecret, ChannelHelper.Token, ChannelHelper.TokenSecret);
  235. }
  236. IClientStatusUpdatesChannel IClientStatusUpdatesChannel.Clone()
  237. {
  238. return new LinkedInClientChannel
  239. {
  240. Hostname = Hostname,
  241. Port = Port,
  242. IsSecured = IsSecured,
  243. MaxConcurrentConnections = MaxConcurrentConnections,
  244. CredentialsProvider = CredentialsProvider
  245. };
  246. }
  247. public string SourceAdress
  248. {
  249. get { return CredentialsProvider.GetCredentials().Claim; }
  250. }
  251. public string AuthMessage { get; private set; }
  252. public ConnectResult Connect()
  253. {
  254. return ConnectResult.Success;
  255. }
  256. public IEnumerable<ChannelFolder> GetFolders()
  257. {
  258. yield return new ChannelFolder("inbox", "inbox", ChannelFolderType.Inbox);
  259. }
  260. public void SelectFolder(ChannelFolder folder)
  261. {
  262. }
  263. public IEnumerable<ChannelMessageHeader> GetHeaders()
  264. {
  265. yield break;
  266. }
  267. public IEnumerable<ChannelMessage> GetMessage(ChannelMessageHeader header)
  268. {
  269. yield break;
  270. }
  271. public bool Disconnect()
  272. {
  273. return true;
  274. }
  275. public IClientInputChannel Clone()
  276. {
  277. LinkedInClientChannel channel = new LinkedInClientChannel();
  278. channel.Hostname = Hostname;
  279. channel.Port = Port;
  280. channel.IsSecured = IsSecured;
  281. channel.MaxConcurrentConnections = MaxConcurrentConnections;
  282. channel.CredentialsProvider = CredentialsProvider;
  283. return channel;
  284. }
  285. public void Dispose()
  286. {
  287. }
  288. }
  289. }