- INFO
-
Si no has leído el articulo anterior sobre publicar en Linkedin usando Gmail, te animo a que lo hagas para tener más contexto
Linkedin, la plataforma del postureo, tiene un API que puedes usar, entre otras cosas, para publicar textos, artículos, imágenes, etc de tal forma que puedes usar herramientas externas o incluso hacerte tu propia integración. En este post vamos a investigar cómo podemos publicar en esta red usando un script en Groovy que podemos planificar
En concreto vamos a ver cómo publicar diariamente la efeméride del CalendarioCientificoEscolar (del cual ya he hablado en alguna otra entrada del blog)
Requisitos
Cuenta en Linkedin
Groovy (y Java) instalados
Un token de Linkedin (ver el post "Publicar en Linkedin desde Gmail" para saber cómo obtenerlo)
Idea
El CalendarioCientificoEscolar es un proyecto open source que mantiene en una serie de ficheros CSV una efemeride por día, con textos en diferentes idiomas y una imagen para cada una. La idea es parsear este fichero, buscar la efemeride del día asi como la imagen asociada y publicarlo en Linkedin usando un script un Groovy
- WARNING
-
En el blog ya he comentado en alguna entrada sobre este proyecto por si quieres conocer más de él
Script
Este es el script a ejecutar. A continuación comentaré las partes más importantes
@Grab('io.github.http-builder-ng:http-builder-ng-core:1.0.4')
@Grab(group='javax.mail', module='mail', version='1.4.7')
import static groovyx.net.http.MultipartContent.multipart
import static groovyx.net.http.HttpBuilder.configure
import static groovyx.net.http.ContentTypes.JSON
import groovyx.net.http.CoreEncoders
import groovyx.net.http.*
import static java.util.Calendar.*
year = args.length > 0 ? args[0] as int : new Date()[YEAR]
month = args.length > 1 ? args[1] as int : new Date()[MONTH]+1
day = args.length > 2 ? args[2] as int : new Date()[DAY_OF_MONTH]
println "Processing $year/$month/$day"
author=System.getenv("LINKEDIN_USER")
accessToken=System.getenv("LINKEDIN_TOKEN")
if( !author || !accessToken ){
println "Necesito la configuracion de telegram"
return
}
http = configure{
request.uri = "https://api.linkedin.com"
request.contentType = JSON[0]
request.headers['Authorization'] = "Bearer $accessToken"
request.headers['X-Restli-Protocol-Version'] = '2.0.0'
}
html = ""
img = ""
['es':'🇪🇸',
'en':'🏴',
'fra':'☫',
'arab':'🇸🇦',
'pt':'🇵🇹',
'gal':'🐙',
'astu':'🐮',
'eus':'🪨',
'cat':'🌊',
'arag':'⛰️',
// 'epo':'🌍',
].each{ kv ->
String lang = kv.key
String emoji = kv.value
String[]found
if( new File("source/csv/${year}/${lang}.tsv").exists() ){
new File("source/csv/${year}/${lang}.tsv").withReader{ reader ->
reader.readLine()
String line
while( (line=reader.readLine()) != null){
def fields = line.split('\t')
if( fields.length != 5)
continue
if( fields[0] as int == day && fields[1] as int == month && fields[2] as int == year){
found = fields
break
}
}
}
}
if(!found){
println "not found $year/$month/$day"
return
}
String title= found[4].split('\\.').first()
String body= found[4].split('\\.').drop(1).join(' ')
if( !img ){
img = "https://calendario-cientifico-escolar.gitlab.io/_/images/${year}/${found[3]}.png"
}
html +="""
$emoji $title
$body
"""
}
html += """
Fuente: Calendario Cientifico Escolar, https://t.me/CalendarioCientifico
"""
json = http.post{
request.uri.path = "/v2/assets"
request.uri.query = ["action":"registerUpload"]
request.body = [
"registerUploadRequest": [
"recipes": [
"urn:li:digitalmediaRecipe:feedshare-image"
],
"owner": "urn:li:person:"+author,
"serviceRelationships": [
[
"relationshipType": "OWNER",
"identifier": "urn:li:userGeneratedContent"
]
],
"supportedUploadMechanism":[
"SYNCHRONOUS_UPLOAD"
]
]
]
}
uploadUrl = json.value.uploadMechanism["com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"].uploadUrl
someFile = File.createTempFile("img","png")
someFile.bytes = img.toURL().bytes
configure{
request.uri = uploadUrl
request.contentType = 'image/png'
request.headers['Authorization'] = "Bearer $accessToken"
request.headers['X-Restli-Protocol-Version'] = '2.0.0'
request.headers['Accept']='*/*'
request.body = someFile
request.encoder('image/png'){ ChainedHttpConfig config, ToServer req->
req.toServer(new ByteArrayInputStream(someFile.bytes))
}
}.put()
post = [
"author": "urn:li:person:"+author,
"lifecycleState":'PUBLISHED',
"visibility": [
"com.linkedin.ugc.MemberNetworkVisibility":'PUBLIC'
],
"specificContent":[
"com.linkedin.ugc.ShareContent":[
"shareCommentary":[
"text": html
],
"shareMediaCategory" : "IMAGE",
"media":[
[status:'READY',media: json.value.asset]
]
]
]
]
http.post{
request.uri.path = "/v2/ugcPosts"
request.body = post
}
Calendario
Como he comentado el calendario se compone de varios ficheros cada uno con una efemeride en un idioma. Lo que hacemos es crear un mapa con los idiomas disponibles e ir concatenando en una cadena los diferentes textos. Cada idioma será un párrafo con un emoji al inicio:
['es':'🇪🇸',
'en':'🏴',
'fra':'☫',
'arab':'🇸🇦',
'pt':'🇵🇹',
'gal':'🐙',
'astu':'🐮',
'eus':'🪨',
'cat':'🌊',
'arag':'⛰️',
// 'epo':'🌍',
].each{ kv ->
String lang = kv.key
String emoji = kv.value
String[]found
if( new File("source/csv/${year}/${lang}.tsv").exists() ){
new File("source/csv/${year}/${lang}.tsv").withReader{ reader ->
...
html +="""
$emoji $title
$body
"""
}
Básicamente una vez ejecutado el bucle tenemos una cadena con el texto que queremos publicar.
Librerías
Las dos primeras lineas del script nos sirven para indicar las dependencias que tiene el mismo. En este caso uso un proyecto http-ng-builder
que me encanta aunque ya está abandonado (creo) y la librería standard de javax mail para poder subir la imagen.
Este proyecto nos proporciona un DSL muy intuitivo para interactuar con servicios remotos.
- Preparamos un objeto
http
con el token de acceso:
http = configure{
request.uri = "https://api.linkedin.com"
request.contentType = JSON[0]
request.headers['Authorization'] = "Bearer $accessToken"
request.headers['X-Restli-Protocol-Version'] = '2.0.0'
}
- Le decimos a Linkedin que queremos subir una imagen:
json = http.post{
request.uri.path = "/v2/assets"
request.uri.query = ["action":"registerUpload"]
request.body = [
"registerUploadRequest": [
"recipes": [
"urn:li:digitalmediaRecipe:feedshare-image"
],
"owner": "urn:li:person:"+author,
"serviceRelationships": [
[
"relationshipType": "OWNER",
"identifier": "urn:li:userGeneratedContent"
]
],
"supportedUploadMechanism":[
"SYNCHRONOUS_UPLOAD"
]
]
]
}
uploadUrl = json.value.uploadMechanism["com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"].uploadUrl
- Linkedin nos proporciona una URL (uploadURL) donde subir nuestra imagen mediante el método
put
:
configure{
request.uri = uploadUrl
request.contentType = 'image/png'
request.headers['Authorization'] = "Bearer $accessToken"
request.headers['X-Restli-Protocol-Version'] = '2.0.0'
request.headers['Accept']='*/*'
request.body = someFile
request.encoder('image/png'){ ChainedHttpConfig config, ToServer req->
req.toServer(new ByteArrayInputStream(someFile.bytes))
}
}.put()
- Publicamos el artículo indicando como atributo el ID de la imagen a adjuntar
post = [
"author": "urn:li:person:"+author,
"lifecycleState":'PUBLISHED',
"visibility": [
"com.linkedin.ugc.MemberNetworkVisibility":'PUBLIC'
],
"specificContent":[
"com.linkedin.ugc.ShareContent":[
"shareCommentary":[
"text": html
],
"shareMediaCategory" : "IMAGE",
"media":[
[status:'READY',media: json.value.asset]
]
]
]
]
http.post{
request.uri.path = "/v2/ugcPosts"
request.body = post
}
Conclusion
Obviamente este script es muy específico para el caso del Calendario pero es muy fácil extraer las partes necesarias para poder crear uno que se adapte a nuestras necesidades y poder publicar en Linkedin de forma desatendida
Top comments (0)