using System; using System.Collections.Generic; using System.Device.Location; using System.Diagnostics; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; using Coding4Fun.Phone.Controls; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Messaging; using Hammock; using MahApps.RESTBase; using MahApps.Twitter; using MahApps.Twitter.Delegates; using MahApps.Twitter.Models; using MahTweets.Core; using MahTweets.Core.Events; using MahTweets.Core.Extensions; using MahTweets.Core.Extensions.MahTweets.Core.Extensions; using MahTweets.Core.Interfaces; using MahTweets.Core.Models; using MahTweets.Core.Repositories; using MahTweets.Core.Services; using MahTweets.Core.UpdateTypes; using MahTweets.Core.Utilities; using MahTweets.Extensions.Twitter.Extensions; using MahTweets.Extensions.Twitter.ViewModels; using MahTweets.Extensions.Twitter.Views; using Tomers.Phone.Controls; namespace MahTweets.Extensions.Twitter { public class Twitter : MahViewModelBase, IMicroblog { private const string ConsumerKey = "NRvCz7KI8MRodk2E28Dmg"; private const string ConsumerSecret = "4Pm8OlyWRUmuWmG25t7yhbtoUW4A9V0CA4XO8tvg"; public readonly TwitterClient TwitterClient; private readonly ContactRepository _contacts; private readonly ILocationService _locationService; private readonly object _ownerLock = new object(); private readonly object _refreshLock = new object(); private readonly List _search; private readonly Func _setupViewModelFactory; private readonly IStatusService _statusService; private Contact _owner; private List _supportedTypes; public Twitter( ContactRepository contacts, Func setupViewModelFactory, IStatusService statusService, ILocationService locationService) { _contacts = contacts; _search = new List(); _locationService = locationService; _setupViewModelFactory = setupViewModelFactory; _statusService = statusService; TwitterClient = new TwitterClient(ConsumerKey, ConsumerSecret, "http://www.mahtweets.com/mobile_oauth.html"); } public static Action OnSuccess { get; set; } public static Action OnFailure { get; set; } #region IMicroblog Members public Action ShowDialog { get; set; } public Uri DarkIcon { get { return new Uri("/MahTweets.Extensions.Twitter;component/Resources/twitter_light.png", UriKind.RelativeOrAbsolute); } } public Uri LightIcon { get { return new Uri("/MahTweets.Extensions.Twitter;component/Resources/twitter_dark.png", UriKind.RelativeOrAbsolute); } } public void Setup(Action onSuccess, Action onError) { OnSuccess = onSuccess; OnFailure = onError; var setup = new TwitterSetupView { DataContext = _setupViewModelFactory() }; ShowDialog(setup); } public void Connect() { TwitterClient.SetOAuthToken(new Credentials { OAuthToken = Credentials.Username, OAuthTokenSecret = Credentials.Password }); IsRefreshing = false; Messenger.Default.Register(this, SubscribeToSearchTwitterHandler); Messenger.Default.Register(this, message => TwitterClient.Statuses.BeginUserTimeline( message.ContactName, (req, res, obj) => { if (obj is List) { var tweets = (List)obj; var results = new List(); foreach (Tweet t in tweets) { Models.Tweet status = t.ToStatus(_contacts); status.ToTimeline(this); _statusService.OnNext(status); results.Add(status); } message.Updates(results); } })); } private void SubscribeToSearchTwitterHandler(SubscribeToSearchTwitter obj) { var existing = _search.Where(l => l.Term == obj.Term); if (!existing.Any()) _search.Add(obj); } public void Disconnect() { } public bool IsRefreshing { get; private set; } public void Refresh(bool isRequired) { lock (_refreshLock) IsRefreshing = true; TaskEx.Run(() => { Messenger.Default.Send(new StartingProgress()); SynchroniseLoadMessages(TwitterClient.Statuses.BeginHomeTimeline, HomeTimelineCallback)(); SynchroniseLoadMessages(TwitterClient.Statuses.BeginMentions, HomeTimelineCallback)(); SynchroniseLoadMessages(TwitterClient.DirectMessages.BeginDirectMessages, HomeTimelineCallback)(); SynchroniseLoadMessages(TwitterClient.DirectMessages.BeginSentDirectMessages, HomeTimelineCallback)(); foreach (var sub in _search) { SubscribeToSearchTwitter s1 = sub; TwitterClient.Search.BeginSearch(s1.Term, (req, res, obj) => { var tweets = obj as List; if (tweets != null) { foreach (var s in tweets) { Contact c = _contacts.FetchOrCreate(s.ContactName); c.ImageUrl = new Uri(s.ContactImage); var t = new Models.Tweet { ID = s.Id.ToString(), Contact = c, Text = HttpUtility.HtmlDecode(s.Text), Time = DateTime.Parse(s.CreatedDate.ToString()).ToLocalTime(), Source = s.Source.StripHtml().GetSourceText(), }; t.HasLinks = t.Text.HasLinks(); t.AddParent(this); t.Types.AddUpdate(this); t.Types.Add(new TwitterSearchType(s1.Term)); _statusService.OnNext(t); } } }); } Messenger.Default.Send(new FinishedProgress()); lock (_refreshLock) IsRefreshing = false; }); } public Contact Owner { get { if (_owner == null) { lock (_ownerLock) { if (_owner == null) { Contact contact = _contacts.Where(c => c.Name == Credentials.AccountName).FirstOrDefault(); if (contact == null) TwitterClient.Account.BeginVerifyCredentials(OwnerCallback); else _owner = contact; } } } return _owner; } set { _owner = value; RaisePropertyChanged("Owner"); } } public void NewStatusUpdate(string text, string id, GeoCoordinate location) { IList outputTweets = TwitterHelper.SplitIntoMessages(text); if (outputTweets.Count > 1) { // multiple messages - format accordingly for (int i = 0; i < outputTweets.Count; i++) { if (i == 0) outputTweets[i] = string.Format("{0}…", outputTweets[i]); else if (i == outputTweets.Count - 1) { string contents; string prefix = TwitterHelper.GetTweetPrefix(outputTweets[i], out contents); outputTweets[i] = string.Format("{0}…{1}", prefix, contents); } else { string contents; string prefix = TwitterHelper.GetTweetPrefix(outputTweets[i], out contents); outputTweets[i] = string.Format("{0}…{1}…", prefix, contents); } } } if (outputTweets.Count > 0) { ThreadPool.QueueUserWorkItem(state => { foreach (string i in outputTweets) { if (string.IsNullOrEmpty(i)) continue; string value = i; Messenger.Default.Send(new StartingProgress()); if (location == null) TwitterClient.Statuses.BeginUpdate(value, id, null, null, (req, res, obj) => { Messenger.Default.Send(new FinishedProgress()); NewTweetCallback(req, res, obj, value, id, null, null); }); else TwitterClient.Statuses.BeginUpdate(value, id, location.Latitude, location.Longitude, (req, res, obj) => { Messenger.Default.Send(new FinishedProgress()); NewTweetCallback(req, res, obj, value, id, location.Latitude, location.Longitude); }); Thread.Sleep(5000); } }); } } public string OwnerName { get { return Credentials.AccountName; } } public bool ReadOnly { get { return false; } } public Credential Credentials { get; set; } public string Name { get { return "Twitter"; } } public string Protocol { get { return "twitter"; } } public IList SupportedTypes { get { if (_supportedTypes == null) { _supportedTypes = new List(); _supportedTypes.AddUpdate(this); _supportedTypes.AddUpdate(this); _supportedTypes.AddUpdate(this); } return _supportedTypes; } } #endregion private static Action SynchroniseLoadMessages(Action beginGet, Action callback) { return () => { var resetEvent = new ManualResetEvent(false); beginGet((request, response, obj) => { resetEvent.Set(); callback(request, response, obj); }); resetEvent.WaitOne(); }; } public void HomeTimelineCallback(RestRequest request, RestResponse response, Object obj) { try { if (obj is List) { foreach (Tweet t in (List)obj) { Models.Tweet status = t.ToStatus(_contacts); status.ToTimeline(this); _statusService.OnNext(status); } } else if (obj is List) { foreach (DirectMessage t in (List)obj) { Contact contact = _contacts.FetchOrCreate(t.Sender.ScreenName); contact.UpdateContact(t.Sender); Contact recipient = _contacts.FetchOrCreate(t.Recipient.ScreenName); recipient.UpdateContact(t.Recipient); Models.DirectMessage status = t.ToStatus(contact); status.Recipient = recipient; status.ToTimeline(this); _statusService.OnNext(status); } } } catch (Exception ex) { Debug.WriteLine(ex); } } public void OwnerCallback(RestRequest request, RestResponse response, Object obj) { if (!(obj is User)) return; var u = (User)obj; _owner = new Contact { Name = u.ScreenName, ID = u.Id.ToString(), ImageUrl = new Uri(u.ProfileImageUrl) }; RaisePropertyChanged("Owner"); } private void NewTweetCallback(RestRequest request, RestResponse response, object obj, string value, string id, double? x, double? y) { try { if (obj is Tweet) { var t = (Tweet)obj; Models.Tweet status = t.ToStatus(_contacts); status.ToTimeline(this); _statusService.OnNext(status); } else if (obj is DirectMessage) { var t = (DirectMessage)obj; Contact contact = _contacts.FetchOrCreate(t.Sender.ScreenName); contact.UpdateContact(t.Sender); Contact recipient = _contacts.FetchOrCreate(t.Recipient.ScreenName); recipient.UpdateContact(t.Recipient); Models.DirectMessage status = t.ToStatus(contact); status.Recipient = recipient; status.ToTimeline(this); _statusService.OnNext(status); } else if (obj is ExceptionResponse) { GlobalDispatcher.Dispatcher.BeginInvoke(() => NotificationBox.Show("Oops!", "Failed to tweet! Twitter may be down, or you may not have a connection", new NotificationBoxCommand("Retry", () => TwitterClient.Statuses.BeginUpdate(value, id, null, null, (ireq, ires, iobj) => NewTweetCallback(ireq, ires, iobj, value, id, x, y))), new NotificationBoxCommand("Save as draft", () => Messenger.Default.Send(new Draft(value))), new NotificationBoxCommand("Discard", () => { }))); } } catch (Exception ex) { //Todo: Logging Debug.WriteLine(ex); } } public void Retweet(string id) { TwitterClient.Statuses.BeginRetweet(id, NewRetweetCallback); } public void NewRetweetCallback(RestRequest req, RestResponse res, object state) { GlobalDispatcher.Dispatcher.BeginInvoke(() => { var toast = new ToastPrompt { Title = "Retweet", Message = "retweeted!" }; toast.Show(); }); } public void Favourite(string id) { TwitterClient.Favourites.BeginCreate(id, NewFavouriteCallback); } public void NewFavouriteCallback(RestRequest req, RestResponse res, object state) { GlobalDispatcher.Dispatcher.BeginInvoke(() => { var toast = new ToastPrompt { Title = "Favourite", Message = "favourited!" }; toast.Show(); }); } public WebRequest DelegatedQuery(string p, TwitterClient.Format format) { return TwitterClient.DelegatedRequest(p, format); } public void Block(string contactName) { TwitterClient.Block.BeginBlock(contactName, (req, res, state) => GlobalDispatcher.Dispatcher.BeginInvoke(() => { var toast = new ToastPrompt { Title = "Blocked", Message = contactName + " is now blocked!" }; toast.Show(); })); } } }