url shortener

How to shorten an url with Bitly asynchronously on Windows Phone

bitly_logo

It was only yesterday when I decided to shorten shared links in UniShare. The reason is not Twitter as suggested by some of my users, but other networks like LinkedIn, that have text length limits as well.

After digging into the Bitly API, I created a little helper that returns the a shorten Bitly-url. In case this is not possible for whatever reason, it returns the long url that was tried to be shortened.

Before we can start to write some code, we need to get our generic access token for the Bitly API. Log in to your Bitly account, and click on ‘Settings’, and choose the ‘Advanced’ tab. If you already verified your mail address, you are nearly done. Enter your password and click on the ‘Generate Token’ Button:

Screenshot (388)

This is one of the constants we need soon. In your project, generate a new class called BitlyHelper. Declare the following constant strings:

private const string bitlyGenericAccessToken = "your_generic_access_token";
private const string base_url = "https://api-ssl.bitly.com/v3/shorten?access_token={0}&longUrl={1}&format=txt";

The base_url string contains the url we are calling, setting our generic access token as well as the long url we want to share. As we are only interested in getting the shortened url, we need to add the ‘format=txt’ part at the end.

The next step is to create an async Task that returns the HttpResponseMessage:

        /// <summary>
        /// Task that starts the async request for the HttpResponseMessage
        /// </summary>
        /// <param name="longUrl">the url to shorten</param>
        /// <returns>the HttpResponseMessage that contains the shortened url</returns>
        private async Task<HttpResponseMessage> getShortUrl(string longUrl)
        {
            //do not forget to UrlEncode the longUrl!
            string request_url = string.Format(base_url, bitlyGenericAccessToken, System.Web.HttpUtility.UrlEncode(longUrl));

            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.IfModifiedSince = DateTime.Now;

            //get the response from the Bitly API
            return await client.GetAsync(new Uri(request_url));
        }

The only important thing we think of here is to UrlEncode our long url, using the System.Web.HttpUtility.UrlEncode() method. If we would not do that, we would get an error on some links from the Bitly API. The rest is pretty straight forward to any other HttpClient usage.

To get our BitlyHelper to be helpful, we are creating another Task that detects the HttpStatusCode of our HttpResponseMessage and returns the shortened url:

        /// <summary>
        /// gets the shortened url out of the HttpResponseMessage
        /// </summary>
        /// <param name="longUrl">the url to shorten</param>
        /// <returns>the shortened url as string</returns>
        public async Task<string> GetShortenedUrl(string longUrl)
        {
            string short_url = string.Empty;

            //using try/catch to avoid Exceptions
            try
            {
                var response = await getShortUrl(longUrl);

                if (response.StatusCode == HttpStatusCode.Ok)
                {
                    short_url = await response.Content.ReadAsStringAsync();
                }
                //on error StatusCodes, just return the longUrl
                else
                {
                    short_url = longUrl;
                }
            }
            catch
            {
                short_url = longUrl;
            }

            return short_url;            
        }

In case the HttpStatusCode is not OK (200), we are simply returning the long url. To avoid any Exceptions, we are doing the same in case there is one. This way, we are keeping it as simple as possible while keeping our desired functionality.

The usage is very simple:

BitlyHelper bitly = new BitlyHelper();
var shorturl = await bitly.GetShortenedUrl("https://yourlongurl.com");

If you want more advanced features, you can perform the whole oAuth dance with your users and get some more features into your app (read more in the API docs). If you just need to shorten the url, this BitlyHelper is all you need.

As always, I hope this is helpful for some of you.

Happy coding!

Posted by msicc in Dev Stories, windev, 0 comments

How to detect all urls in a string to match Twitter’s requirements (Windows 8(.1) and Windows Phone 8(.1))

url

As you might guess, I am still working on that app I mentioned in my last blog post. As I was diving deeper into the functions I want, I recognized that Twitter does a very well url handling server side.

Like the official documentation says, every url will be shortened with a link that has 22 characters (23 for https urls).

I was trying to write a RegEx expression to detect all the links that can be:

  • http
  • https
  • just plain domain names like “msicc.net”

This is not as easy as it sounds, and so I was a bit struggling. I then talked with @_MadMatt (follow him!) who has a lot of experience with twitter. My first attempt was a bit confusing as I did first only select http and https, then the plain domain names.

I found the names by their domain ending, but had some problems to get their length (which is essential). After the very helpful talk with Matthieu, I finally found a very good working RegEx expression here on GitHub.

I tested it with tons of links, and I got the desired results and it is now also very easy for me to get their length.

Recovering the amount of time I needed for this, I decided to share my solution with you. Here is the method I wrote:

        public int CalculateTweetCountWithLinks(int currentCount, string text)
        {
            int resultCount = 0;

            if (text != string.Empty)
            {
                //detailed explanation: https://gist.github.com/gruber/8891611
                string pattern = @"(?i)\b((?:https?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\-]+[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)/)(?:[^\s()<>{}\[\]]+|\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\))+(?:\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\)|[^\s`!()\[\]{};:'.,<>?«»“”‘’])|(?:(?<!@)[a-z0-9]+(?:[.\-][a-z0-9]+)*[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)\b/?(?!@)))";

                //generating a MatchCollection
                MatchCollection linksInText = Regex.Matches(text, pattern, RegexOptions.Multiline);

                //going forward only when links where found
                if (linksInText.Count != 0)
                {
                    //important to set them to 0 to get the correct count
                    int linkValueLength = 0;
                    int httpOrNoneCount = 0;
                    int httpsCount = 0;

                    foreach (Match m in linksInText)
                    {
                        //https urls need 23 characters, http and others 22
                        if (m.Value.Contains("https://"))
                        {
                            httpsCount = httpsCount + 1;
                        }
                        else
                        {
                            httpOrNoneCount = httpOrNoneCount + 1;
                        }

                        linkValueLength = linkValueLength + m.Value.Length;
                    }

                    //generating summaries of character counts
                    int httpOrNoneReplacedValueLength = httpOrNoneCount * 22;
                    int httpsReplacedValueLength = httpsCount * 23;

                    //calculating final count
                    resultCount = (currentCount - linkValueLength) + (httpOrNoneReplacedValueLength + httpsReplacedValueLength);                    
                }
                else
                {
                    resultCount = currentCount;
                }
            }
            return resultCount;
        }

First, we are detecting links in the string using the above mentioned RegEx expression and collect them in a MatchCollection.

As https urls have a 23 character length on t.co (Twitter’s url shortener), I am generating two new counts – one for https, one for all other urls.

The last step is to substract the the length of all Match values and add the newly calculated replaced link values lengths.

Add this little method to your TextChanged event, and you will be able to detect the character count on the fly.

As always, I hope this is helpful for some of you.

Happy coding, everyone!

 

Posted by msicc in Dev Stories, windev, 0 comments