Пользователь
0,0
рейтинг
24 октября 2011 в 18:03

Разработка → Кросспостинг в twitter, facebook, livejournal, vkontakte tutorial

ASP*

Когда я писал проект crafthunters.com, я заметил что для раскрутки клиенты используют социальные сети. Пользовались виджетами и лайками, но по хорошему надо было попадать в ленту новостей. Кроме того, популярный вконтакте вывел новости на главную страницу в сентябре. Т.е. для распространения контента надо было адаптировать standalone блог для представления в социальных сетях, используя простую истину: попасть в ленту новостей популярных социальных сетей. Вначале это происходило вручную и приносило более половины траффика. Потом пришла идея это всё автоматизировать.
Популярными социальными сетями у нас были:
  • facebook
  • вконтакте
  • livejournal
  • твиттер

Предстояло настроить автоматический кросспостинг в каждую из социальных сетей, который до этого вели вручную. Нужно еще уточнить, что размещать записи в социальных сетях необходимо было не у себя на стене\бложике, а в группе\сообществе.

Далее я хочу представить реализацию кросспостинга под эти четыре социальные сети для asp.net mvc.


0. Основные принципы.



Перво-наперво необходимо получить права и сохранить их в базе данных для последующего использования. Вторым шагом было установка в какую группу\сообщество будет добавляться контент (задача по сути только для вконтакта). Третьим шагом было создание записи в группе\сообществе с активной ссылкой на оригинальный пост.

Для твиттера, фейсбука и вконтакте используется API на основе OAuth и для работы с ними надо было зарегистировать приложение в каждой социальной сети для этих целей.

Чтобы не повторяться, я сразу расскажу как происходит работа c OAuth (для twitter xAuth). Вначале мы передаем id приложения и список прав, которые хотим получить. Список прав — это к каким ресурсам мы разрешаем доступ нашего приложения. Выглядит это так:



После разрешения, мы получаем код по которому получаем токен доступа. К каждому обращению на сервер мы передаем этот токен и тем самым провайдер услуг (вконтакте, фейсбук и твиттер) знают что нам можно доверять и разрешают совершить действие (в нашем случае — кросспостинг).

1. Livejournal



ЖЖ уже давно используется для кросспостинга и делает он это через XML-RPC команды.
Почитав документацию (http://www.livejournal.com/doc/server/ljp.csp.xml-rpc.protocol.html) использовав библиотеку для .net xml-rpc (http://www.xml-rpc.net/) я приступил к реализации.

Создаем интерфейс взаимодействия:

public interface ILj : IXmlRpcProxy 
{   
	[XmlRpcMethod("LJ.XMLRPC.login")]   
	LjUserInfo Login(UserPassword user); 

	XmlRpcMethod("LJ.XMLRPC.postevent")] 
	PostLjAnswer Post(PostLj post); 
}


Получение доступа



Логин и пароль передаются в открытом виде и передаются по протоколу веб-сервиса. Создаем класс для получения прав.
public class UserPassword
{   
	[JsonProperty("username")]
	public string username { get; set; } 

	[JsonProperty("password")]
	public string password { get; set; }   

	public int ver   {
		get     {
			return 1; 
		} 
	} 
}


При каждой публикации логин и пароль также передаются, так что метод Login по сути только проверяет правильность пары логин\пароль.

Проверка доступа.


public LjUserInfo Auth(UserPassword username) 
{ 
	ILj proxy = XmlRpcProxyGen.Create<ILj>(); 
	var ans = proxy.Login(username);
	return ans; 
} 

Ответ ans мы анализируем на положительный ответ иначе выдаем ошибку.

Добавление записи столь же тривиально:
public void Publish(UserPassword username, Post message, string ljgroup = null) 
{ 
	ILj proxy = XmlRpcProxyGen.Create<ILj>();  
	var post = new PostLj();   
	post.username = username.username;   
	post.password = username.password;   
	post.ver = 1;   
	post.@event = message.Content;   
	post.subject = message.Title;   
	post.lineendings = "pc";   
	post.year = DateTime.Now.Year;   
	post.mon = DateTime.Now.Month;   
	post.day = DateTime.Now.Day;   
	post.hour = DateTime.Now.Hour;   
	post.min = DateTime.Now.Minute;   
	if (!string.IsNullOrWhiteSpace(ljgroup))   {  
		post.usejournal = ljgroup;   
	}   else   {     
		post.usejournal = username.username;   
	}   
	var ans = proxy.Post(post); 
}

Собственно всё очень просто. Конечно, есть очень много дополнительных опций и библиотека достаточно обширна, но задача — добавить наш пост в ЖЖ — решается легко.

Что не понравилось — передача пароля в открытом виде или в md5 по незащищеному каналу.

2. Твиттер



Для твиттера я использовал популярную библиотеку www.twitterizer.net но для начала надо было зарегистрировать приложение по адресу: dev.twitter.com/apps.

Права которые будет запрашивать приложение — как на чтение так и на запись. Кроме всего прочего, приложение получает бесконечный по сроку истечения токен для использования. Этот токен необходимо сохранить в БД. (Таблица Social.JsonResource)

Получение прав присходит по следующему сценарию:
public string Authorize(string redirectTo) 
{   
	OAuthTokenResponse requestToken = OAuthUtility.GetRequestToken(Config.twitterConsumerKey, Config.twitterConsumerSecret, redirectTo);   
	// Direct or instruct the user to the following address:   
	Uri authorizationUri = OAuthUtility.BuildAuthorizationUri(requestToken.Token);   
	return authorizationUri.ToString(); 
} 

Мы передаем два ключа (ключ приложения и секретный ключ), и переходим на страницу твиттера для подтверждения прав. Твиттер запрашивает права и возвращает нас по адресу redirectTo куда дополнительно передает код. По этому коду мы получаем токен доступа. Далее мы обновляем статус (создаем короткое сообщение) через приложение:
public void Publish(Post post) 
{   
	var tokens = new OAuthTokens();   
	tokens.ConsumerKey = Config.twitterConsumerKey;   
	tokens.ConsumerSecret = Config.twitterConsumerSecret;   
	tokens.AccessToken = twitterAccessToken.Token;   
	tokens.AccessTokenSecret = twitterAccessToken.TokenSecret;   
	TwitterStatus.Update(tokens, post.TwitterText); 
}


3. Facebook



Регистрируем приложение по адресу: developers.facebook.com/apps

Получаем токен доступа. Вначале отправляем для получения разрешения прав:
public string Authorize(string redirectTo) 
{   
	return string.Format(AuthorizeUri, Config.AppId, redirectTo); 
} 

public ActionResult GetFbCode() 
{   
	var fbSocial = currentUser.Socials.Where(p => p.Provider == "facebook").FirstOrDefault();   
	if (fbSocial != null)   {
		return RedirectToAction("Index");  
	}   else   {    
		return Redirect(fbProvider.Authorize("http://" + HostName + "/Social/SaveFbCode"));   
	} 
}

Обрабатываем код для получения токена доступа:
public ActionResult SaveFbCode() 
{   
	if (Request.Params.AllKeys.Contains("code")) {
		var code = Request.Params["code"];     
		if (ProcessFbCode(code))     
		{
			return RedirectToAction("Index");     
		}   
	}   
	return View("CantInitialize"); 
} 

protected bool ProcessFbCode(string code) {   
	if (fbProvider.GetAccessToken(code, "http://" + HostName + "/Social/SaveFbCode"))   
	{ 
		var jObj = fbProvider.GetUserInfo();    
		var fbUserInfo = JsonConvert.DeserializeObject<FbUserInfo>(jObj.ToString()); 
		var fbAccess = new FbAccessToken() 
		{ 
			AccessToken = fbProvider.AccessToken     
		};
		var jsonFbAccess = JsonConvert.SerializeObject(fbAccess);   
		var fbSocial = currentUser.Socials.Where(p => p.Provider == "facebook").FirstOrDefault();  
		if (fbSocial == null)  
		{      
			fbSocial = new Models.Social() 
			{       
				UserID = currentUser.ID,   
				JsonResource = jsonFbAccess.ToString(),
				Provider = "facebook",       
				UserInfo = jObj.ToString()    
			};  
			repository.CreateSocial(fbSocial);     
		}   
		else     
		{  
			fbSocial.UserInfo = jObj.ToString();      
			repository.UpdateSocial(fbSocial);  
		}   
		return true; 
	}  
	return false; 
}

Тут важная деталь при получении бессрочного токена доступа мы в параметре запроса параметров должны запросить &scope=...,offline тем самым избавив себя запрашивать этот токен постоянно, но для большей секретности этот фрагмент можно переделать и для использования токена с истекаемым сроком годности.

Создание поста


public ActionResult CrossPostFb(int id) 
{   
	var post = repository.Posts.Where(p => p.ID == id).FirstOrDefault();  
	var fbSocial = currentUser.SocialGetByProvider("facebook"); 
	if (post != null && post.UserID == currentUser.ID && fbSocial != null)  
	{    
		var postSocial = new Social.Post();     
		if (!string.IsNullOrWhiteSpace(post.PreviewUrl))  
		{ 
			//ссылка на изображение
			postSocial.Preview = "http://" + HostName + post.PreviewUrl; 
		} 
		postSocial.Title = post.Title; 
		postSocial.Teaser = post.Subtitle;     //ссылка на оригинал статьи 
		postSocial.Link = "http://" + HostName + "/Post/" + post.ID.ToString();     //устанавливаем сохраненые токены 
		var fbAccess = JsonConvert.DeserializeObject<FbAccessToken>(fbSocial.JsonResource);   
		fbProvider.AccessToken = fbAccess.AccessToken;  
		//публикуем пост  
		fbProvider.Publish(postSocial);   
		repository.CrossPost(post, Post.CrossProvider.facebook); 
	}  
	return RedirectToAction("Index");
}

В зависимости от установленных параметров создастся запись. При установке активной ссылки facebook самостоятельно выберет нужную ему картинку

4. Вконтакте



Вконтакте предлагает самый непростой алгоритм взаимодействия.

Начнем по порядку: создавать посты как у себя на стене так и на стене группы может только Standalone приложение (а не веб), поэтому регистрировать приложение (http://vkontakte.ru/editapp?act=create&site=1) нужно указывать [x] Standalone-приложение.

Второе: для добавления к записи картинки ее нужно загрузив предварительно.

Третье: для загрузки картинки необходимо запросить адрес сервера по которому будет производится загрузка.

Четвертое: ссылка на оригинальный пост размещается в параметре attachments и при добавлении ссылки не гарантирует добавление заметки.

Собственно остальное всё так же, так что приступим и получим права:
public ActionResult GetVkCode()
{ 
	var vkSocial = currentUser.Socials.Where(p => p.Provider == "vkontakte").FirstOrDefault();  
	if (vkSocial != null)  
	{    
		return RedirectToAction("Index"); 
	} 
	else 
	{    
		return Redirect(vkProvider.Authorize("http://" + HostName + "/Social/SaveVkCode"));   
	} 
} 

public string Authorize(string redirectTo) 
{   
	return string.Format(AuthorizeUri, Config.AppKey, redirectTo); 
}


Для нашего кросспостинга мы запрашиваем следующие уровни доступа: photos, groups, wall, offline.
Т.е. можем загружать фоточки в группы на стену в любое время.

Публикация



Перед публикацией на стену группы нам нужно узнать номер этой группы по имени (groups.getById). Удивительно то, что группы (а также персональные страницы) нумеруются в отрицательную сторону. Т.е.
после получения результата значение gid надо умножить на -1.

Потом мы запрашиваем сервер для загрузки фотографий: photos.getWallUploadServer, а не photos.getUploadServer — хоть они и идентичны почти.

Далее мы через post по полученному url-запросу посылаем фотографию на сервер вконтакте. Я использовал библиотеку UploadHelper (http://aspnetupload.com/).

После загрузки на сервер надо передать команду о сохранении этого изображения: photos.saveWallPhoto — на что получаем id фотографии. Если вы использовали photos.getUploadServer вместо photos.getWallUploadServer — то фотография не сохранится.

И уже следующим этапом мы добавляем фотографию на стену группы/или персональной страницы (wall.post).

Собственно всё.

5. Плюшки



Попробовать можно тут: http://cocosanka.ru (после регистрации, можно записать что-то бессмысленное в блог, и потом откросспостить).

Скачать исходники тут: https://bitbucket.org/chernikov/cocosanka2

Для работы с данными я использовал библиотеку Json.net(http://json.codeplex.com/) переводя автоматически полученные строки в объекты.

Ключи приложений хранятся в Web.config, но необходимо будет прописать свои.
chernikov @chernikov
карма
177,9
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (31)

  • 0
    Молодец! :) Я пока увы использую связку: Опубликовал Пост — > нажал на опубликовать в ВК (оттуда уже экспорт в Twitter, Facebook, LiveJournal идет автоматический. (настройки)
    • 0
      Тут проблема в том, что нужно было в сообщества(для жж)\Персональный блог(для фейсбука) писать, а не к себе на стену.
      • 0
        это я уловил, читая топик :) Вот из сорцов можно будет сделать на сколько возможно интеграцию полную)
  • 0
    все отлично, но что за ужасные междустрочные отступы в листинге кода?
    • 0
      Пользовался Хабра-редактором (http://www.bankinform.ru/habraeditor/ ), не знаю чего так поросло всё.
  • 0
    Ура! Еще больше смошного спама в блогах и соцсетях!
  • 0
    Не совсем понял из текста — токен вконтакте имеет все-таки ограниченный срок действия или нет?
    • 0
      В данном решении\примере — бессрочный токен (приложение и запрашивает доступ offline). Спрашивает один раз, потом пользуется. Но можно переделать по-другому.
      • 0
        И все-равно непонятно — он бессрочный, или правильный ответ как в вашем же комменте ниже? Все собираюсь прикрутить, но как раз смущает тот факт, что токен НЕ бессрочный.
        • 0
          Для вконтакта:
          Документация
          — тут запрашиваем &scope=offline (токен бессрочный)
          Для фесбука:
          Документация — аналогично &scope=access_offline (токен бессрочный)
          Для твиттера:
          Токен бессрочный всегда
          • 0
            Теперь все предельно ясно, спасибо.
  • 0
    Проблема всех кросспостилок в одном: они не могут закачивать фотографии на разные хостинги для разных сетей. Фотография как правило одна, и везде указывается просто ссылкой.
    • 0
      Фейсбук сам выкачивает по ссылке. На сервер вконтакта мы закачиваем. По идее можно и с twipic api разобраться и связать с твиттером, но задачи такой не стояло. В ЖЖ лучше использовать прямую ссылку с оригинального хостинга.
  • 0
    емнип, у вконтакте токен протухает через 24 часа. отображение этой формочки запроса доступа происходит каждый раз? :(
    • 0
      Нет, потом молча выдает свежий. Фишка в том, чтобы я зная токен доступа через месяц не написал на стене пользователя пошлости.
      • 0
        Это ведь если юзер не нажал «Выйти»? Иначе при перезапросе токена его попросят залогиниться в социалке.
        На счет «через месяц», IMO, сомнительно — ничто не мешает юзеру убрать доступ приложению в настройках на сайте, вот тогда и токен прибить логично. Твиттер (и вроде бы Фейсбук) не выставляет таких жестких сроков жизни.
        • 0
          Если пользователь не залогинен, ничего не мешает приложению действовать от его имени, по сроку действия токена.

          Если пользователь убирает доступ в настройках — тогда да, но вот в фейсбуке — это надо знать где нажать, чтобы убрать (Главная -> Настройки конфиденциальности -> Приложения и веб-сайты), а это достаточно далеко и не очевидно.
      • 0
        хм. прочитал ваш комментарий выше, спасибо. «со стороннего сервера» сбило — думал опция «offline» для не-standalone приложений. тест подтвердил: «expires_in=0»
  • 0
    >но для большей секретности этот фрагмент можно переделать
    >и для использования токена с истекаемым сроком годности.
    может «безопасности» вместо «секретности» было бы корректнее?
    • 0
      Тоже не очень, это же зависит от доверия пользователя к приложению и устойчивости к взлому БД хранящей токены.
      Честно говоря пока пользователи не обращают на это внимание (какие именно права запрашивает приложение).
      Но я например стараюсь не давать подобные права, когда не уверен что именно будет с ними делать приложение (тот же спам может разослать).
      Если не нужен оффлайн доступ и пользователи сторонние — то лучше не запрашивать. В нашем случае — админ просто делегирует свои права, это легко объяснить, и тут не возникает боязни.
  • 0
    Я правильно понял, что нужно авторизироваться через любую соц. сети, зайти в настройки, включить твиттер, вконтакте, facebook и жж (последнее я не включал), зайти в Блог, написать свое послание и радоваться жизни? Если да, то послание не дошло :)
    • 0
      В блоге появятся кнопочки для кросспоста по тем соцсетям на которые вы дали доступ.
      • 0
        Да-да, чуть ниже написал, что все получилось, не сразу дошло.
    • 0
      Извиняюсь, все работает.

      Хотя в твиттер кидается ссылка, которая ведет на 404.
  • 0
    Я хочу писать посты в твиттер (сой личный) и чтобы они кросспостились в facebook, вконтакте, google+ (так же личные страницы).

    А ещё я хочу загружать альбом фотографий в пикасу, и чтобы он автоматически добавлялся в facebook и вконтакте.

    Техника до сих пор до этого не дошла?
    • 0
      Google+ пока сделали API только на просмотр данных.
      Picasa — отказываются от использования API.
      Но есть flickr.
      Создавать альбомы фотографий в фейсбуке и вконтакте можно.

      • 0
        >>Создавать альбомы фотографий в фейсбуке и вконтакте можно.
        Одновременно? Как?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.