diff options
author | Sergey Nazaryev <sergey@nazaryev.ru> | 2016-01-25 19:36:50 +0300 |
---|---|---|
committer | Sergey Nazaryev <sergey@nazaryev.ru> | 2017-04-02 22:04:57 +0300 |
commit | bd00cf94dabd0330e3ef758a440d68541bf312d6 (patch) | |
tree | e87d900a0c15b4a24b51996c36b35715d0317c0c | |
download | vk2rss-bd00cf94dabd0330e3ef758a440d68541bf312d6.zip vk2rss-bd00cf94dabd0330e3ef758a440d68541bf312d6.tar.gz vk2rss-bd00cf94dabd0330e3ef758a440d68541bf312d6.tar.bz2 |
Initial release.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.md | 28 | ||||
-rw-r--r-- | utils.lua | 29 | ||||
-rwxr-xr-x | vk2rss.lua | 56 | ||||
-rw-r--r-- | vkfeed.lua | 165 |
5 files changed, 279 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f40bd5e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +access_token diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f86cbc --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +vk2rss -- граббер ленты сообществ и пабликов ВКонтакте в RSS +============================================================ + +Программа vk2rss создана для конвертации ленты из паблика или группы ВКонтакте +в формат RSS. Иногда это бывает полезно, если у вас уже есть RSS-ридер, в +котором вы бы хотели видеть посты какого-нибудь редкого сообщества, у которого +нет отдельного сайта с экспортом их ленты в RSS. + +Зависимости +----------- + +- lua +- cjson (установить можно через luarocks) +- wget || curl + +Как использовать +---------------- + +./vk2rss <название_группы/паблика> [access token] + +На stdout: XML в RSS-формате с постами (последние 100 штук). +В случае, если группа закрытая, необходимо предоставить access token. + +Access token +------------ + +Для его получения обратитесь к инструкции на стр. `https://new.vk.com/dev/auth_mobile` +Пример: `https://oauth.vk.com/authorize?client_id=1&display=page&scope=offline&redirect_uri=https://oauth.vk.com/blank.html&response_type=token&v=5.52` diff --git a/utils.lua b/utils.lua new file mode 100644 index 0000000..56492f4 --- /dev/null +++ b/utils.lua @@ -0,0 +1,29 @@ +function string.nl2br(input) + return input:gsub( "\n", "<br />" ) +end + +function string.starts(input, start) + return input:sub( 1, string.len( start ) ) == start +end + +function io.readAll(file) + local f = io.open(file, "rb") + local content = f:read("*all") + f:close() + return content +end + +function os.download( url ) + file = os.tmpname() + os.execute( "/usr/bin/wget -qO- '" .. url .. "' > " .. file ) + data = io.readAll( file ) + os.remove( file ) + + return data +end + +function fatal( string ) + print( string ) + os.exit( 2 ) +end + diff --git a/vk2rss.lua b/vk2rss.lua new file mode 100755 index 0000000..f611f40 --- /dev/null +++ b/vk2rss.lua @@ -0,0 +1,56 @@ +#!/usr/bin/lua + +local xml = require "xml" + +dofile( "vkfeed.lua" ) +dofile( "utils.lua" ) + +function usage() + print( "usage: vk2rss <domain> [token]" ) + os.exit(1) +end + +function main() + local argc = table.maxn( arg ) + if( argc < 1 ) then + usage() + end + + local domain = arg[1] + local access_token = nil + + if( argc == 2 ) then + access_token = arg[2] + end + + local feedInfoJson = getFeedInfo( domain, access_token ) + local feedInfo = parseFeedInfo( feedInfoJson ) + local feedJson = getFeed( domain, 100, 0, access_token ) + local feed = parseFeed( feedInfo["name"], feedJson ) + + if( feed == nil ) then + fatal( "Can't get feed from " .. domain ) + end + + local rss = { xml = 'rss', version="2.0", + { xml = 'channel', + { xml = 'title', feedInfo["name"] }, + { xml = 'description', feedInfo["description"] }, + { xml = 'link', feedInfo["url"] }, + }, + } + + for key, item in ipairs( feed ) do + table.insert( rss[1], { xml = 'item', + { xml = 'title', item["title"] }, + { xml = 'pubDate', item["date"] }, + { xml = 'link', item["link"] }, + { xml = 'description', item["description"] }, + }) + end + + print( '<?xml version="1.0" encoding="utf-8"?>' ) + print( xml.dump( rss ) ) +end + +main() diff --git a/vkfeed.lua b/vkfeed.lua new file mode 100644 index 0000000..767e544 --- /dev/null +++ b/vkfeed.lua @@ -0,0 +1,165 @@ +dofile( "utils.lua" ) + +local json = require "cjson" + +local API_VK_SERVER = "https://api.vk.com/" +local VK_BASE_DOMAIN = "https://vk.com/" + +function parseFeed( feedName, jsonText ) + local items = {} + + object = json.decode( jsonText ) + if( object["response"] == nil ) then + return nil + end + + for k, item in ipairs( object["response"]["items"] ) do + if (item["is_pinned"] ~= 1) then + local link = VK_BASE_DOMAIN .. "/wall" .. item["owner_id"] .. "_" .. item["id"] + local description = item["text"] + local date = os.date( "%a, %d %b %Y %X GMT", item["date"] ) + + local photos = {} + local links = {} + local videos = {} + + if( item["attachments"] ~= nil ) then + for k, attach in ipairs( item["attachments"] ) do + if( attach["type"] == "photo" ) then + + local attachInfo = attach["photo"] + local photoSizes = getSortPhotos( attachInfo ) + table.insert( photos, { small = photoSizes[3], large = photoSizes[ table.maxn( photoSizes ) ] } ) + + elseif( attach["type"] == "link" ) then + + local attachInfo = attach["link"] + local photo = nil + if ( attachInfo["photo"] ~= nil ) then + local photoSizes = getSortPhotos(attachInfo["photo"]) + photo = photoSizes[1] + end + table.insert( links, { photo = photo, + url = attachInfo["url"], + title = attachInfo["title"] } ) + + elseif( attach["type"] == "video" ) then + local attachInfo = attach["video"] + local photoSizes = getSortPhotos( attachInfo ) + local url = VK_BASE_DOMAIN .. "/video" .. attachInfo["owner_id"] .. "_" .. attachInfo["id"] + table.insert( videos, { photo = photoSizes[2], + url = url, + title = attachInfo["title"] } ) + end + end + end + + table.insert( items, + createItem( date, title, description, link, videos, photos, links ) ) + end + end + + return items +end + +function getFeed( domain, count, offset, access_token ) + local url = "" + if( tonumber( domain ) ~= nil ) then + url = API_VK_SERVER .. "/method/wall.get?owner_id=-" .. domain .. "&offset=" .. offset .. "&count=" .. count .. "&v=5.44" + else + url = API_VK_SERVER .. "/method/wall.get?domain=" .. domain .. "&offset=" .. offset .. "&count=" .. count .. "&v=5.44" + end + + if( access_token ~= nil ) then + url = url .. "&access_token=" .. access_token + end + return os.download( url ) +end + +function getFeedInfo( domain, access_token ) + local url = API_VK_SERVER .. "/method/groups.getById?group_id=" .. domain .. "&fields=city,country,place,description,wiki_page,members_count,counters,start_date,finish_date,can_post,can_see_all_posts,activity,status,contacts,links,fixed_post,verified,site,ban_info&v=5.44" + if( access_token ~= nil ) then + url = url .. "&access_token=" .. access_token + end + return os.download( url ) +end + +function parseFeedInfo( jsonText ) + local object = json.decode( jsonText ) + if( object["response"] == nil ) then + return nil + end + + local feed = object["response"][1] + local info = {} + + info["name"] = feed["name"] + info["description"] = feed["description"] + + if feed["screen_name"] ~= nil then + info["url"] = VK_BASE_DOMAIN .. "/" .. feed["screen_name"] + elseif feed["type"] == "page" then + info["url"] = VK_BASE_DOMAIN .. "/public" .. feed["id"] + elseif feed["type"] == "group" then + info["url"] = VK_BASE_DOMAIN .. "/group" .. feed["id"] + end + + return info +end + +function createItem( date, title, description, link, videos, photos, links ) + local photoMaxHeight = 100 + if( table.maxn(photos) == 1 ) then + photoMaxHeight = nil + end + + photosHtml = "<p>" + for k, photo in ipairs(photos) do + if photoMaxHeight ~= nil then + photosHtml = photosHtml .. "<a href='" .. photo["large"] .. "'><img hspace='3' height=".. photoMaxHeight .." src='" .. photo["small"] .. "'/></a>" + else + photosHtml = photosHtml .. "<a href='" .. photo["large"] .. "'><img hspace='3' src='" .. photo["small"] .. "'/></a>" + end + end + photosHtml = photosHtml .. "</p>" + + linksHtml = "<p>" + for k, link in ipairs(links) do + if link["photo"] ~= nil then + linksHtml = linksHtml .. "<a href='" .. link["url"] .. "'><img hspace='6' src='" .. link["photo"] .. "'/>" .. link["title"] .."</a>" + else + linksHtml = linksHtml .. "<a href='" .. link["url"] .. "'>" .. link["title"] .."</a>" + end + end + linksHtml = linksHtml .. "</p>" + + videosHtml = "<p>" + for k, video in ipairs(videos) do + videosHtml = videosHtml .. "<a href='" .. video["url"] .. "'><img hspace='3' src='" .. video["photo"] .. "'/><p>" .. video["title"] .."</p></a>" + end + videosHtml = videosHtml .. "</p>" + + return { title = title, + pubDate = date, + link = link, + description = description:nl2br() .. photosHtml .. videosHtml .. linksHtml } +end + +function getSortPhotos(photo) + local sizes = {} + for key in pairs(photo) do + if( key:starts( "photo_" ) ) then + local size = tonumber( key:sub( string.len( "photo_" ) + 1 ) ) + table.insert( sizes, size ) + end + end + + table.sort( sizes ) + + local photos = {} + for k, size in ipairs(sizes) do + table.insert( photos, photo[ "photo_" .. size ] ) + end + + return photos +end |